变量提升
这里先给出变量提升的定义。
1. 变量提升的定义
所谓的变量提升,是指在JavaScript代码执行的过程中,JavaScript引擎变量的声明(使用var)和函数的声明部分提升到代码开头的“行为”。
变量被提升后,会给变量设置默认值,这个默认值就是我们熟悉的undefined
。变量提升是JavaScript中比较基本的知识点,只有深入理解变量提升打好JavaScrip技术基础,才能更快地排查代码执行中出现的异常。
2. 理解变量提升
2.1 JavaScript代码的执行顺序
先思考一个问题,JavaScript是按照顺序执行的吗?比如下面这样一段代码:
showMsg(); // 1
console.log(name); // 2
var name = '小明';
function showMsg () {
console.log('吃得苦中苦');
}
复制代码
如果以上代码完全是按照顺序执行的,那么1和2的位置应该会报错,因为访问不到函数showMsg和变量name,但是实际运行这段代码发现,1位置的代码运行可以打印出吃得苦中苦
的字符串,2位置的代码运行打印的结果是undefined。
所以,代码并不是完全按照顺序执行的,其中肯定是发生了什么,改变了某些代码执行的顺序的。
2.2 JavaScript代码的执行流程
要搞懂这个问题,就要先了解JavaScript代码的执行流程,一段JavaScript代码在执行之前会被JavaScript引擎编译,编译完成之后,才会进入执行阶段,整个流程如下图所示。
JS代码经过编译后的产出是执行上下文和可执行代码。这里的执行上下文是可执行代码运行的环境,如果对执行上下去不太理解,可以参考这篇文章JavaScript的执行上下文,执行上下文包含变量环境和词法环境两部分。当JS引擎执行可执行代码时,就会去执行上下文中寻找代码运行所需要的变量。
画图演示2.1中代码的编译和执行过程如下图所示。
从上图可以看到,经过编译之后,name变量的声明和showMsg函数的声明,被放到了执行上下文中的变量环境中,并且name变量被赋值了初始值undefined
,当JS引擎执行代码时,在变量环境中就会访问到变量name和函数showMsg。
2.3 相同的变量或函数如何处理
当代码中出现相同的变量或函数怎么办,如以下代码
showMsg();
console.log(name);
var name = '小明'; // 第1次,声明name变量
var name = '小王'; // 第2次,声明name变量,虽然不会报错但是这并不是好的实践
console.log(name);
function showMsg () { // 第1次,声明showMsg函数
console.log('吃得苦中苦');
}
function showMsg () { // 第2次,声明showMsg函数,会将第一次声明的覆盖掉
console.log('方为人上人');
}
复制代码
JavaScript允许使用var重复定义一个变量,并不会报错,但这不是好的实践,不推荐这种写法。如果定义了两个相同的函数,那么最终存放在变量环境中的是最后定义的那个函数,后面定义的函数会覆盖掉之前定义的。
3. 总结
- JS的执行机制是,先编译再执行,编译为执行阶段生成执行上下文和可执行代码;
- 在编译阶段,变量和函数会被存放到变量环境中;变量默认值是
undefined
,在执行阶段,JS引擎会从变量环境中去查找自定义的变量和函数; - 当定义两个名称相同的函数时,后面的会覆盖掉前面的。
这里注意,被提升的变量是使用var定义的,使用let定义的变量不会被提升
核心记住这么一句话
先编译,再执行,变量函数被提升