你不知道的JavaScript(上卷)--提升

162 阅读3分钟

我们在直觉上会认为 JavaScript 代码在执行时是由上到下一行一行执行的。但实际上这并不完全正确,有一种特殊情况会导致这个假设是错误的。

考虑以下代码:

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

你认为 console.log(..) 声明会输出什么呢? 很多人会认为是 undefined,因为 var a 声明在 a = 2 之后,他们自然而然地认为变量被重新赋值了,因此会被赋予默认值undefined。但是,真正的输出结果是 2。

考虑另外一段代码:

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

鉴于上一个代码片段所表现出来的某种非自上而下的行为特点,你可能会认为这个代码片 段也会有同样的行为而输出 2。还有人可能会认为,由于变量 a 在使用前没有先进行声明, 因此会抛出 ReferenceError 异常。

不幸的是两种猜测都是不对的。输出来的会是 undefined。

编译器

引擎会在解释 JavaScript 代码之前首先对其进行编译。编译阶段中的一部分工作就是找到所有的声明,并用合适的作用域将它们关联起来。

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

var a = 2;这条代码JavaScript会将其看成两个声明:var a;和a = 2;前者是在编译阶段进行的。后者赋值声明会被留在原地等待执行阶段。

我们的第一个代码片段会以如下形式进行处理:

var a; //编译
 
a = 2; //执行
 
console.log( a );

类似地,我们的第二个代码片段实际是按照以下流程处理的:

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

这个过程就好像变量和函数声明从它们在代码中出现的位置被“移动” 到了最上面。这个过程就叫作提升。

先声明后赋值!!

不只是变量会被提升,函数也会被提升,函数内部的变量也会被提升。

函数提升以及函数内部变量提升示例代码:

 foo(); 
 
 function foo() {        
     console.log( a ); // undefined        
     var a = 2;    
 }

上述代码提升后的执行顺序

function foo() {     
    var a; 
    console.log( a ); // undefined 
    a = 2;   
} 
 
foo();

函数优先

函数声明和变量声明都会被提升。但是函数会首先被提升,然后才是变量。

示例代码:

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 ); 
};

var foo 尽管出现在 function foo()... 的声明之前,但它是重复的声明(因此被忽 略了),因为函数声明会被提升到普通变量之前。

尽管重复的 var 声明会被忽略掉,但出现在后面的函数声明还是可以覆盖前面的。如下代码示例:

foo(); // 3 
 
function foo() {     
	console.log( 1 ); 
}
 
var foo = function() {     
	console.log( 2 ); 
}; 
 
function foo() {     
	console.log( 3 ); 
}

后面的3会覆盖前面的1。

在JavaScript中,var foo(..)只会提升foo变量名,并不会提升foo后面的(..)里面的内容。而function foo()则会将foo变量名和(..)里内容一起提升,所以function foo()里的内容会比var foo先执行,而后面的function foo()也会覆盖前面的同名函数从而导致此片段代码最后的输出结果是3。