先有鸡还是先有蛋
JavaScript 代码在执行时是由上到下一行一行执行的。考虑以下代码:
a = 2;
var a;
console.log( a );
先谈谈我个人初学时看到这段代码的理解:
1.首先a被隐式声明为全局变量,并将2赋值给a;
2.var a;重新声明了a;
3.最后打印a;a为undefined;
然而最后打印a的值为2;那么到底发生了什么?到底是声明在前,还是赋值在前?
逐步分析
回顾js编译原理。引擎会在解释 JavaScript 代码之前首先对其进行编译。编译阶段中的一部分工作就是找到所有的声明,并用合适的作用域将它们关联起来。
总结的来说就是变量和函数在内的所有声明都会在任何代码被执行前首先被处理。所以上述代码实际是按照以下流程处理
var a;
a = 2;
console.log(a);
这个过程就好像变量和函数声明从它们在代码中出现的位置被“移动”到了最上面。这个过程就叫作提升。
PS:只有声明本身会被提升,而赋值或其他运行逻辑会留在原地。
函数声明与函数表达式
foo();
function foo() {
console.log( a ); // undefined
var a = 2;
}
使用function关键字声明一个函数;函数也会提升。而函数表达式却不会被提升。
foo(); // 不是 ReferenceError, 而是TypeError!
var foo = function bar() {
console.log('success');
};
上述函数表达式代码块代码实际是按照以下流程处理
var foo;//undefined
foo();//对undefined进行()操作;会抛出TypeError错误
foo = function bar(){
console.log('success');
};
函数优先
函数声明和变量声明都会被提升。函数会首先被提升,然后才是变量。举例如下代码。
foo();
var foo;
function foo() {
console.log( 1 );
}
foo = function() {
console.log( 2 );
};
上述代码块代码实际是按照以下流程处理:
function foo() {
console.log( 1 );
}
foo(); // 1
foo = function() {
console.log( 2 );
};
PS:重复声明会被忽略 var foo;
总结:
无论作用域中的声明出现在什么地方,都将在代码本身被执行前首先进行处理。可以将这个过程形象地想象成所有的声明(变量和函数)都会被“移动”到各自作用域的最顶端,这个过程被称为提升。
声明本身会被提升,而包括函数表达式的赋值在内的赋值操作并不会提升。
参考文献
《你不知道的JavaScript》