在 JS 中,我们直觉上认为代码是从上往下一行一行执行的,但实则不然,因为在编译阶段存在变量提升这个机制。
提升机制
我们知道,引擎会在解释 JavaScript
代码之前首先对其进行编译。编译阶段中的一部分工作就是找到所有的声明,并用合适的作用域将它们关联起来(词法作用域的核心)
比如var a = 2
,JavaScript
实际上会将其看成两个声明:第一个定义声明var a
是在编译阶段进行的。第二个赋值声明a = 2
会被留在原地等待执行阶段。这个过程就好像变量和函数声明从它们在代码中出现的位置被“移动” 到了最上面。这个过程就叫作 提升
需要注意:1)只有声明本身会被提升,而赋值或其他运行逻辑会留在原地;2)函数声明会被提升,但是函数表达式却不会被提升
优先级
函数声明和变量声明都会被提升。但是一个值得注意的细节是 函数会首先被提升,然后才是变量
比如:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17foo(); // 1
var foo;
function foo() {
console.log( 1 );
}
foo = function() {
console.log( 2 );
};
// 这个代码片段会被引擎理解为如下形式
function foo() {
console.log( 1 );
}
foo(); // 1
foo = function() {
console.log( 2 );
};
注意,var foo 尽管出现在 function foo()...
的声明之前,但它是重复的声明(因此被忽略了),因为函数声明会被提升到普通变量之前。
小结
1.什么是变量提升?
引擎在解释代码时,会有编译和执行两个阶段。在编译阶段,会把所有的变量和作用域确定好;在第执行阶段才会进行赋值相关操作。比如,像var a = 1
会被分解为两个单独的声明var a
和a = 1
。普通函数声明会整个提升到顶部。
这意味着无论作用域中的声明出现在什么地方,都将在代码本身被执行前首先进行处理。 可以将这个过程形象地想象成所有的声明(变量和函数)都会被“移动”到各自作用域的最顶端,这个过程被称为提升
所以要注意:1)避免重复声明,特别是当普通的
var
声明和函数声明混合在一起的时候,会引起问题;2)if
等语句中的声明也会被提升,以后 JS 中的未来版本甚至会改变这个行为,所以也要避免。
2.变量提升的优先级?
在一个作用域中,会有哪些变量呢?除了上面的普通变量声明和函数声明,还有参数这个变量。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15function someFn(a) {
console.log(a)
var a = 2
function a() {}
}
someFn(1) // function a() {}
// 实则是这样
function someFn(a) {
function a() {}
var a // 因为变量 a 已声明,此步会被忽略
console.log(a)
a = 2
}
我们可以发现优先级 参数 > 函数 > 变量