在JavaScript中提升包括函数提升和变量提升。
函数提升: 是指在代码编译时函数的声明会被提升到代码的顶部;
变量提升: 在js里,不仅函数可以提升,变量也会被提升,但函数的提升优先于变量的提升;
1、是先有的鸡 还是先有的蛋
示例-1:
a = 666;
var a;
console.log(a); // 666
对, 结果是“666”。可能有人会错以为是undefined, 因为var a;声明在a=666;之后。误以为变量被重新的赋值了。因此,被赋予的默认值为undefined。但是,这是错的。实际输出的是“666”,因为在编译的时候var a;会被提升到这段代码的顶部。所以,上面代码等价于。
// 上面的代码等价于
var a;
a = 666;
console.log( a );
示例-2: 在考虑下下面的代码,会输出什么?
console.log(a);
var a = 8;
答案是,输出的是undefined。因为变量a的声明会被提升到console.log(a)之前。即等价于下面代码。
var a;
console.log( a );
a = 8;
2、追其根本
深入理解这个问题的话就要回溯到编译阶段,对于JavaScript引擎来说,在解释js代码之前首先会对源码编译,而在编译这个阶段里有一部分的工作就是找到所有的声明(包括变量声明、函数声明等..),并用合适的作用于将他们关联起来。
在简单点说就是,包括变量和函数等的所有的声明都会在任何代码被执行之前首先被处理。打个比方,这个过程就好像变量和函数声明从它们在代码中出现的位置被移动到了最上面,这个过程就叫”提升“。(注意: 只有声明本身被提升,而赋值和其它逻辑会留在原地。)
现在,再理解上面示例代码就显而易见了。
所以,总结一句话,就是“先有的蛋(声明),后有的鸡(赋值)”
变量提升原理清晰之后,再来看看函数提升!
示例-3:
test(); // 在这里,函数test()的调用在它自身声明之前
function test() {
console.log(a); // undefined
var a = 6;
}
实际上上面这段代码会被理解为下面的形式
function test() {
var a;
console.log(a); // undefined
a = 6;
}
test();
实例-4:
foo(); // TypeError
bar(); // ReferenceError
var foo = function bar() {
// .......
};
上面这段代码实际上回被理解为:
var foo;
foo(); // TypeError
bar(); // ReferenceError
foo = function() {
var bar = ....self...
// ...
}
3、函数优先
函数优先即是当函数和变量声明都需要提升时函数声明提升优先于变量声明提升。换句话说就是函数先提升,之后才是变量。
示例-5:
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()…..声明的前面,但是它和函数foo重名了,即是重复的声明,所以会被忽略掉。
换句话来说就是,函数声明被提升优先于变量,也就是先提升的函数会覆盖掉之后被提升的重名变量。
实例-6
foo(); // 3
function foo() {
console.log(1);
}
var foo = function() {
console.log(2);
};
function foo() {
console.log(3);
}
这个示例说明,尽管重复的var声明会被覆盖掉,但是出现在后面的同名函数声明确可以覆盖前面的。
4、总结
这里有几点需要注意的:
1、 每个作用域都会进行提升。
2、 只有声明本身被提升,而赋值和其它逻辑会留在原地。
3、 函数首先被提升,之后才是变量。
4、函数声明会被提升,但是函数表达式却不会被提升。