声明提升就好像把声明(变量声明和函数声明)从它们所在代码中出现的位置移到了最上面。直觉上我们会以为JavaScript代码是由上到下一行一行执行的,但实际上这并不完全正确,还要考虑声明提升的存在。
1. 考虑下面关于变量声明提升的代码:
a = 2;
var a;
console.log(a); // ??
按照由上到下一行一行执行的思路:var a 声明在 a = 2 之后,所以a被重新赋值了,最后会输出undefined?
console.log(a);
var a = 2; // ??
按照由上到下一行一行执行的思路:变量a在使用前没有先声明,所以会抛出ReferenceError异常?
代码执行一下的话,我们会发现答案是2 和 undefined,所以上面的思路是行不通的。正确的思路是:包括变量和函数在内的所有声明都会在任何代码被执行前首先被处理,就好像变量和函数声明会从它们所在代码中出现的位置被“移动”到了最上面,这个过程也就是提升。
我们习惯把var a = 2; 看做一个声明,而实际上JavaScript引擎并不这么认为。它将 var a 和 a = 2 当做两个单独的声明, 第一个是编译阶段的任务,而第二个则是执行阶段的任务。只有声明本身会被提升,而赋值或其他运行逻辑会留在原地。
按照声明提升的思路(相当于把所有声明提升的部分移到代码的最上面然后在一行一行顺序执行),上面的两段代码相当于:
var a;
a = 2;
console.log(a); // 2
var a;
console.log(a); // undefined
a = 2;
这样的话,再按照由上到下一行一行执行的思路,答案很明显就是2 和 undefined了。
2. 考虑下面关于函数声明提升的代码:
fn();
function fn(){
console.log(a); // ??
var a = 2;
}
按照声明提升的思路(相当于把所有声明提升的部分移到代码的最上面然后在一行一行顺序执行),上面的两段代码相当于:
function fn(){
var a;
console.log(a); // undefined
a = 2;
}
fn();
这样的话,再按照由上到下一行一行执行的思路,答案就是undefined了。
注:函数声明会被提升,但是函数表达式却不会被提升。
fn();
var fn = function (){
console.log(a);
var a = 2;
}
// Uncaught TypeError: fn is not a function
函数声明和变量声明都会被提升,但是函数会首先提升,然后才是变量。即函数声明的优先级要大于变量声明。
3. 考虑下面关于声明提升优先级的代码:
var foo;
foo(); // ??
function foo(){
console.log(1);
}
按照声明提升的思路(相当于把所有声明提升的部分移到代码的最上面然后在一行一行顺序执行),上面的两段代码相当于:
function foo(){
console.log(1);
}
var foo; // 重复声明,被忽略
foo(); // 1
尽管var foo出现在function foo之前,但是函数提升的优先级要大于变量提升,所以 function foo(){} 会在var foo之前;但是var foo是重复声明所以被忽略了,所以最终输出1。
注: 尽管重复的var声明会被忽略掉,但出现在后面的函数声明还是可以覆盖前面的。
var foo;
foo(); // 3
function foo(){
console.log(1);
}
function foo(){
console.log(3);
}
foo = function(){
console.log(2);
}
4. 考察声明提升的例子
alert(a); // ??
a(); // ??
var a = 3;
function a(){
alert(10)
}
alert(a) // ??
a = 6;
a(); // ??
答案:
alert(a); // function a(){ alert(10) }
a(); // 10
var a = 3;
function a(){
alert(10)
}
alert(a) // 3
a = 6;
a(); // Uncaught TypeError: a is not a function
参考阅读
- 你不知道的JavaScript上卷>第4章提升
- JavaScript系列文章:变量提升和函数提升