声明提升

一般认为,JS 代码在执行时是由上到下一行一行执行的。但实际上,这并不完全正确,主要是因为声明提升的存在。


变量声明提升

a = 2;
var a;
console.log(a);

直觉上认为会是undefined,因为var a声明在a=2;之后,可能变量被重新赋值(默认值undefined)了,但实际结果为2。

console.log(a);
var a = 2;

鉴于上面的特点,可能会认为这个代码片段也会输出a,但实际结果为undefined。

所有这些和观感相违背的原因在于:编译器的编译过程。JS 引擎会在解释代码前首先对其编译,编译阶段中的一部分工作就是找到所有的声明,并用合适的作用域将它们关联起来。

包括变量和函数在内的所有声明都会在任何代码被执行前首先被处理。


var a = 2;
 
这个代码片段实际上包括两个操作:var a 和 a = 2
第一个定义声明是在编译阶段由编译器进行的;第二个赋值操作会被留在原地等待引擎在执行阶段执行。
 
var a;
a = 2;
console.log(a);

声明从它们在代码中的位置被“移动”到最上面,这个过程就叫做提升(hoisting)。  // 每个作用域都会进行提升操作。


函数声明提升

声明包括两种:变量声明和函数声明。不仅变量声明可以提升,函数声明也可以提升。

foo();
function foo(){
	console.log(1);  // 1
}
 
此代码片段之所以在控制台输出1,就是因为foo()函数进行了提升。

需要注意的是:函数声明会提升,但函数表达式却不会提升,即使是具名的函数表达式也无法被提升。


[ 函数覆盖 ] 变量声明和函数声明都会被提升,但函数声明会覆盖变量声明。

var a;
function a(){}
console.log(a);  // 'function a(){}'

但,如果变量存在赋值操作,最终结果为变量的值。

var a =1;
function a(){}
console.log(a);  // 1

需要注意的是:变量的重复声明是无用的,但函数的重复声明会覆盖前面的声明(无论是变量还是函数声明)。

应该避免在同一作用域中重复声明。