JavaScript引擎的工作方式是,先解析代码,获取所有被声明的变量,然后再一行一行地运行。
这造成的结果,就是所有的变量的声明语句,都会被提升到代码的头部,这就叫做变量提升(hoisting),准确的说是变量声明提升。
先来看一下什么是声明:
声明宣称一个名字的存在,定义则为这个名字分配存储空间,而初始化则是为名字分配的存储空间赋初值。
1 | var v; // 声明变量v |
举个栗子:
1 | var v = "hello"; |
这段代码运行的结果是什么呢?
答案是:undefined
JavaScript是没有块级作用域的。函数是JavaScript中唯一拥有自身作用域的结构。
在function作用域内,变量v的声明被提升了。所以这段代码相当于:
1 | var v = "hello"; |
请注意,变量提升只对var命令声明的变量有效,如果一个变量不是用var命令声明的,就不会发生变量提升。
1 | console.log(b); |
上面的语句将会报错,提示“ReferenceError: b is not defined”,即变量b未声明,这是因为b不是用var命令声明的,JavaScript引擎不会将其提升,而只是视为对顶层对象(如windows对象)的b属性的赋值。
声明提升
当前作用域内的声明都会提升到作用域的最前面,包括变量和函数的声明
1 | (function(){ |
变量a,f,b,c的声明会被提升到函数作用域的最前面,类似如下:
1 | (function(){ |
请注意函数表达式并没有被提升,这也是函数表达式与函数声明的区别。进一步看二者的区别:
1 | (function(){ |
上面代码中函数声明f2被提升,所以在前面调用f2是没问题的。虽然变量f1也被提升,但f1提升后的值为undefined,其真正的初始值是在执行到函数表达式处被赋予的。所以只有声明是被提升的。