第4章:提升
只有声明本身被提升了,而任何赋值或者其他的执行逻辑都被留在原处。
函数声明和变量声明都会被提升。但函数会首先被提升,然后才是变量。重复的函数声明(避免在同一个作用域内的重复定义),后续的函数声明确实会覆盖前一个。
1 变量声明提升
举例:
a = 2;
var a;
console.log( a ); //2
// 1. var a;
// 2. a = 2;
// 3. console.log( a );
复制代码
console.log( a ); //undefined
var a = 2;
// 1. var a;
// 2. console.log( a );
// 3. a = 2;
复制代码
这种看起来不正常顺序的方式,编译器会帮助引擎整理。
当你看到 var a = 2;
时,你可能认为这是一个语句。但是 JavaScript 实际上认为这是两个语句:var a;
和 a = 2;
。第一个语句,声明,是在编译阶段被处理的。第二个语句,赋值,为了执行阶段而留在 原处。
2 函数相关提升
对比上面的例子:
foo();
function foo() {
console.log(a); // undefined
var a = 2;
}
复制代码
函数 foo
的声明(在这个例子中它还包含一个隐含的、实际为函数的值)被提升了,因此第一行的调用是可以执行的。
函数声明会被提升,就像我们看到的。但是函数表达式不会。
foo(); // 不是 ReferenceError, 而是 TypeError!
var foo = function bar() {
// ...
};
复制代码
变量标识符 foo
被提升并被附着在这个程序的外围作用域(全局),所以 foo()
不会作为一个 ReferenceError
而失败。但 foo
还没有值(如果它不是函数表达式,而是一个函数声明,那么它就会有值)。所以,foo()
就是试图调用一个 undefined
值,这是一个 TypeError
—— 非法操作。
函数声明和变量声明都会被提升,函数会首先被提升,然后才是变量。
foo(); // 1
var foo;
function foo() {
console.log(1);
}
foo = function () {
console.log(2);
};
复制代码
1
被打印了,而不是 2
!这个代码段被引擎解释执行为:
function foo() {
console.log(1);
}
foo(); // 1
foo = function () {
console.log(2);
};
复制代码
关于重复函数声明被覆盖的坑:
foo(); // "b"
var a = true;
if (a) {
function foo() { console.log("a"); }
}
else {
function foo() { console.log("b"); }
}
复制代码
复习
我们可能被诱导而将 var a = 2
看作是一个语句,但是 JavaScript 引擎 可不这么看。它将 var a
和 a = 2
看作两个分离的语句,第一个是编译期的任务,而第二个是执行时的任务。
这将导致在一个作用域内的所有声明,不论它们出现在何处,都会在代码本身被执行前 首先 被处理。你可以将它可视化为声明(变量与函数)被“移动”到它们各自的作用域顶部,这就是我们所说的“提升”。
声明本身会被提升,但不是赋值,即便是函数表达式的赋值,也 不会 被提升。
要小心重复声明,特别是将一般的变量声明和函数声明混在一起 —— 如果你这么做的话,危险就在眼前!