我们先提前预告一下变量提升的几个要点,然后在根据后面的例子进一步阐述:
- 变量提升是按照作用域为单位的;
- 函数的提升优先于变量的提升;
- 存在多个相同函数名的函数会被覆盖;
- 函数表达式并不会被提升。
章节直通车:
什么是变量提升
可能有些小伙伴不清楚什么是变量提升,我们以一个简单的例子来说明一下:
console.log( a );
var a = 2;
大家可以自己思考或者直接试验一下,这里打印的a为undefined。
我们在第一章中说道编译器的基础三步骤,其一就是分词、词法分析,在此阶段编译器会把一些变量和相应的作用域进行关联,在作用域中提前声明,我们可以把上面的代码稍微变动如下:
编译阶段:
var a;
执行阶段:
console.log( a );
a = 2;
所以这时的a已经被定义,输出的时候就是undefined。这个就叫做变量提升。
变量提升是按照作用域为单位的
foo();
function foo() {
console.log( a ); // undefined
var a = 2;
}
在全局作用域中,foo函数被提升;
在foo函数中,变量a被提升。
console.log(c); // undefined
if (true) {
var c = 1;
}
注意,if的大括号并不是作用域,所以这里的作用域还是全局作用域,变量c被提升了。(上一章节有具体介绍JavaScript作用域)
本来准备按照《你不知道的JavaScript》书中以if大括号和函数为例来说明,但是现在es6已经改变了这种做法,在if条件中的函数会默认为函数表达式,函数表达式并不会被提升如下:
foo(); // "b"
var a = true;
if (a) {
function foo() { console.log( "a" ); }
}
else {
function foo() { console.log( "b" ); }
}
现在这里已经不像书中说的输出“b”了,而是undefined。
当然对于老版本这种操作也是非常不友好的,并不推荐使用,这里只是提一嘴。
函数的提升优先于变量的提升
foo(); // 1
var foo;
function foo() {
console.log( 1 );
}
foo = function() {
console.log( 2 );
};
上面很清晰的可以看到函数foo的声明是优先于变量的,否则会报错TypeError。我们可以将上面的代码修改为提升后的样子:
function foo() {
console.log( 1 );
}
// var foo; 由于上面foo已经被声明了,这一条就被直接过滤了
foo(); // 1
foo = function() {
console.log( 2 );
};
存在多个相同函数名的函数会被覆盖
foo(); // 3
function foo() {
console.log( 1 );
}
var foo = function() {
console.log( 2 );
};
function foo() {
console.log( 3 );
}