关于编译器,引擎会在解释 JS 代码之前首先对其进行编译。编译阶段的一部分工作就是找到所有的声明,并用合适的作用域将它们关联起来。
1.声明提升
“提升”就是变量和函数声明从它们在代码中出现的位置被“移动”到了最上面,这个过程就叫“提升”。
// 代码示例 1
foo();
function foo() {
console.log(a); // undefined
var a = 2;
}
如上面代码示例 1, foo
函数的声明被提升了,因此第一行中的调用可以正常执行。需要注意的是,每个作用域都会进行提升操作。也就是函数自身也会在内部对 var a 进行提升,所以最终的形式可以理解为如下代码示例 2:
// 代码示例 2
function foo() {
var a;
console.log(a); // undefined
a = 2;
}
foo();
2.函数表达式不会被提升
// 代码示例 3
foo(); // TypeError 类型错误
var foo = function bar() {
// ...
}
foo()
由于对 undefined 值进行函数调用而导致非法操作,因此抛 TypeError 出异常。
3.函数优先
函数声明会被提升到普陀变量之前。
// 代码示例 4
foo(); // 1
var foo;
function foo() {
console.log(1);
}
foo = function() {
console.log(2);
}
上述代码示例 4 会被引擎理解为如下形式:
function foo() {
console.log(1);
}
foo(); // 1
foo = function() {
console.log(2);
}
重复的函数声明可以覆盖前面的。
尽可能避免在块内部声明函数。
总结
-
var a = 2;
其中var a;
是编译阶段的任务,a = 2;
是执行阶段的任务; - 所有的声明(变量和函数)都会被“移动”到各自作用域的最顶端,这个过程叫“提升”;
- 声明本身会被提升,而包含函数表达式在内的赋值操作不会被提升;
- 避免重复声明。
注:文章参考总结自 《你不知道的 JavaScript 上卷》[美] KYLE SIMPSON 著 第 42 页。