理解JavaScript变量声明行为的一个好办法是把变量声明看作由两部分组成,即声明和赋值。JavaScript隐式地提升(hoists)声明部分到封闭函数的顶部,而将赋值留在原地。
换句话说,变量的作用域是整个函数,但仅在var语句出现的位置进行赋值
函数的声明方式
在定义一个函数的时候通常有两种声明方式:
不同之处
函数表达式后面加括号可以直接执行
函数声明会提前预解析
预解析
让我们先看一个例子:
可以看到,函数声明foo被预解析了,它可以在其自身代码之前执行;而函数表达式foo_later则不能。要解决这个问题,我们先要弄清楚JavaScript解析器的工作机制。
变量提升(hoist)
JavaScript解析器会在自身作用域内将变量和函数声明提前(hoist),也就是说,上面的例子其实被解析器理解解析成了以下形式:
|
这样也就可以解释,为什么在函数表达式之前调用函数,会返回错误了,因为它还没有被赋值,只是一个未定义变量,当然无法被执行。
同样的,我们也可以试着猜测下面这段代码的输出结果:
该段代码可以被解析成一下形式:
|
变量声明被提到最前(所以不会报出变量不存在的错误),但赋值没有被提前,所以第一次的输出结果是undefined。
需要注意的是
由于函数声明会被预解析,所以不要使用此种方法来声明不同函数。尝试猜想下面例子的输出结果:
|
与我们预想的不同,该段代码弹出的是“2”.这是因为两个函数声明在if语句被执行之前就被预解析了,所以if语句根本没有用,调用aaa()的时候直接执行了下面的函数。
总结
通过上面的讲解可以总结如下:
变量的声明被提前到作用域顶部,赋值保留在原地
函数声明整个“被提前”
函数作为值赋给变量时只有变量“被提前”了,函数没有“被提前”
通过练习上面的实例自己多感受一下。另外,作为最佳实践:变量声明一定要放在作用域/函数的最上方(JavaScript 只有函数作用域!)。
参考:
JavaScript 中对变量和函数声明的“提前(hoist)”
函数声明 VS 函数表达式