深入理解 javascript 变量声明提升 (hoisting)

2016.05.30

Javascript在代码处理上分为上下两个阶段,第一,这个阶段创建变量,函数声明以及形式参数。这是解析和进入上下文的阶段。

第二阶段是代码运行时执行过程,创建函数表达式和不合格的标识符(未定义变量)。

但为了实际应用的目的,我们采用了“提升(hoisting)”的概念,这个概念并没有在 ECMAScript 标准中定义,但却经常来用来表述这种情形。

例如这一段代码:

1
2
3
4
5
var myName = "chase-young"
;(function(){
console.log(myName) //undefined
var myName = "ycarus"
})()

讲道理,控制台输出的该是 chase-young,然而却是undefined,甚至连ycarus 都不是。

这是因为在匿名函数内部,变量 myName 的声明被“提升”到了函数顶部声明,覆盖掉了函数外声明的 myName 变量。

然而在提升并声明的过程中,并没有给覆盖声明的变量赋值,导致控制台输出的undefined.

所以之前的代码相当于:

1
2
3
4
5
6
var myName = "chase-young"
;(function(){
var myName = undefined
console.log(myName) //undefined
myName = "ycarus"
})()

类似的,函数声明和函数表达式的区别在于函数表达式并不会连带其中的代码一起被提升,而只是提升变量声明,例如:

1
2
3
4
5
6
7
(function(){
f1() //Uncaught TypeError: f1 is not a function
f2() //Uncaught ReferenceError: f2 is not defined
f3() //OK
var f1 = function(){}
function f3(){}
})()

因此,为了避免这类因变量声明提升所引发的混乱,最好在一开始就用 var 声明所有的变量。

这样有以下优点:

  • 提供一个单一地址查找到函数所有需要的局部变量
  • 防止因函数声明所引发的逻辑错误
  • 帮助牢记要声明的变量,尽可能地少适用全局变量