提升
函数作用域和块级作用域的行为是一样的,可以总结为:任何声明在某个作用域内的变量,都将附属于这个作用域。
先有鸡还是先有蛋
看一个栗子
很多人会认为结果是undefined
,理由是第三行的变量定义会覆盖掉a原来的值,但是真正的结果是2.
在看另外一个栗子
你可能会认为这里的值是2或者抛出错误,但是这里的值时undefined
。
编译器再度来袭
为了搞清楚到达是先有鸡还是先有蛋,我们需要回顾一下这个问题。
在第一章节我们讲解了关于编译器的内容,引擎牛仔解释javascript代码之前首先对其进行编译,而编译阶段中的工作就包含了找到所有的声明,并用合适的作用域将他们关联起来。
所以我们可以预想到的是,引擎在编译阶段,会将所有的变量声明和函数声明提前进行处理,就像上面的例子var a = 2
,这里看似是一个声明,但是在引擎看来他是var a;
和a = 2
这两个声明的结合体。其中var a;
是在编译阶段进行的,而第二个声明会留在原地等待执行阶段。
上面第一段代码可以理解为
类似的第二段代码也是按照如下的流程进行处理的
这也就不难解释上面的运行结果为什么是undefined
了。
类似变量声明,函数声明也会被相应的在编译阶段被处理(提升),但是对于函数表达式来说却不会被提升。
看一段代码:
上面的代码中,根据我们结合上诉的理解,其实它的执行流程如下:
其中,由于函数表达式是不会在编译阶段被提升的,所以上诉代码中第二行报了TypeError
错误,而不是RefferenceError
。这是因为,foo
变量声明被提前,第二行foo( )
对foo的RHS查找找到了foo
这个变量,但是这个变量此时还没有指向函数,所以也就是会报TypeError
函数优先
值得注意的一个细节是,函数会先于变量提前。应该尽量避免在块内部声明函数。要注意避免重复声明,特别是当普通的var
声明和函数声明混合子啊一起的时候,否则会引起很多危险的问题!