什么是变量提升?
变量提升(Hoisting)是人们对JavaScript执行上下文工作方式的一种认识,并不是官方给出的改变。从字面上理解,变量提升的意思是变量和函数的声明会在物理层移动到作用域的最前面,虽然这样理解并不准确,效果是相同的。变量提升实际的实现方式是JavaScript的变量和函数的声明会在编译阶段放入内存。这意味着使用者在正式声明一个函数或者变量之前就能够使用它。
要搞清楚变量提升的实现,首先我们要明确以下2点:
- javascript代码并不是一行一行往下执行的.
- javascript执行分为2个步骤:
- 编译(词法解释/预解释)
- 执行
变量提升帮助理解
console.log(a);
var a = 'ghostwu';
对于上面的代码这个例子,第一行代码,你可能认为报错, 因为在输出a之前,没有定义a变量, 但是正确的结果是undefined.。根据上面js执行代码的解释,结合实际的代码,当我们碰到 var a = "ghostwu" 定义一个变量的时候, 其实js把这句话看成是2个阶段的事, var a 发生在编译阶段, a = 'ghostwu'发生在执行阶段. 然后 var a会被提升到当前作用域的最前面, a = 'ghostwu'留在原地等待执行阶段,所以看下面的案例:
1 a = 'ghostwu';
2 var a;
3 console.log( a );
4
5 //上面这段代码经过编译之后,变成下面这样
6
7 var a; //被提升到当前作用域的最前面
8 a = 'ghostwu'; //留在原地,等待执行
9 console.log( a ); //输出ghostwu
10 console.log( a );
12 var a = 'ghostwu';
13
14 //上面这段代码,经过编译之后,变成下面这样
15
16 var a;
17 console.log( a );//输出undefined,而不会报错
18 a = 'ghostwu';
函数声明提升
在讲解函数声明提升之前,我们先来了解函数的常见的两种定义方式
1 //函数声明, 形如:
2 function show(){
3 console.log( '函数声明方式' );
4 }
5
6 //函数表达式, 形如:
7 var show = function(){
8 console.log( '表达式方式' );
9 }
因为函数表达式和函数的声明,在编译阶段,会产生不同的解释效果,所以函数的声明会被提升,案例见下面代码:
1 show();
2 function show(){
3 console.log( a );
4 var a = 'ghostwu';
5 }
//函数声明会被提升,所以上面的代码经过编译之后,就变成下面这样
6 function show(){ //函数声明被提升到 当前作用域的最前面
7 var a;
//var声明被提升到当前作用域的最前面, 注意,他不会提升到函数的外面, 因为当前的作用域是在函数中
8 console.log( a );
9 a = 'ghostwu';
10 }
11 show();//输出undefined
但是函数表达式是不会被提升的,看下面的例子:
1 show(); //报错,show is not a function
2 var show = function(){
3 console.log( 'ghostwu' );
4 }
5 //对于上面这段表达式代码,经过编译之后:
6 var show;
7 show(); //执行之后就是 undefined(), 所以在表达式定义之前,调用函数报错了
8 show = function(){
9 console.log( 'ghostwu' );
10 }
但是看下面的案例:
1 show(); //你好
2 var show;
3 function show(){
4 console.log( '你好' );
5 }
6 show = function(){
7 console.log( 'hello' );
8 }
上面的代码为什么会输出“你好”,因为当出现同名的函数声明,变量声明的时候, 函数声明会被优先提升,变量声明会被忽略。 所以经过编译之后,就变成:
1 function show(){
2 console.log( '你好' );
3 }
4 show(); //你好
5 show = function(){
6 console.log( 'hello' );
7 }
8 show();//如果这里在调用一次,就是hello, 因为show函数体在执行阶段被重新赋值了
但是如果有同名的函数声明,后面的会覆盖前面的,如下代码:
1 show(); //how are you
2 var show;
3 function show(){
4 console.log( 'hello' );
5 }
6 show = function(){
7 console.log( '你好' );
8 }
9 function show(){
10 console.log( 'how are you!' );
11 }
12 //上面的代码经过编译之后,变成如下形式:
13 function show(){
14 console.log( 'how are you!' );
15 }
16 show(); //how are you
17 show = function(){
18 console.log( '你好' );
19 }
20 show(); //如果在这里再执行一次,结果:你好
注:
- 变量提升只是提升变量的声明,并不会把赋值也提升上来。
- 正因为有变量提升这回事,所以为了避免变量提升带来的不好的影响,我们最好在定义变量时,使用let而不是var。