如果你看不懂为什么输出是4,那你看我上面这个图只会觉得更抽象。
这个有点复杂,涉及到es5变迁到es6的上古遗留问题,里面包含了变量和函数的提升规则,块级作用域内函数声明提升的映射规则,还有块级作用域的默认变量提升规则。
谈这个问题之前要先了解,在es6出现块级作用域时,因为要考虑到已经发布的老代码兼容问题,所以没办法改变块级作用域内函数的处理规则,所以仍然保留了函数的三种行为方式
即1:允许在块级作用域内声明函数
2:函数声明类似于var,会提升到全局或函数作用域的头部
3:同时,函数声明还会提升到所在块级作用域的头部
块级作用域函数,就像预先在全局作用域中使用var声明了一个变量,且默认值为undefined。
疑难点一:函数声明为什么影响了全局a的赋值
这道题function a并不会直接提升到全局,而是先提升到块级顶部,但问题关键点在于,他是什么时候提升到全局的。
答案是window.a只有等块级作用域中函数声明的定义的那行代码执行过之后,才会被映射到全局作用域。
疑难点二:全局为什么输出的是4而不是5
看图
全局输出的为什么function a(){}? why? 难道不应该是10吗。首先,块级作用域函数a的声明会被提升到全局作用域,然后在块级作用域中,由于声明函数a提升到块级作用域顶端,所以打印a = function a(){},而window.a由于并没有执行函数定义的那一行代码,所以仍然为undefined。当执行到声明函数定义的时候,就会把函数a映射到全局作用域中。当执行a = 10的时候,JS引擎会进行LHS查找,此时,声明函数已经被同时提升到全局作用域和块级作用域顶端了,由于遮蔽效果,此时查找a只会找到块级作用域内的a,并不会找到全局作用域的a,这时,a已经被定义,a = 10只会执行赋值操作,并不会进行提升。
疑难点三:window.a === a为什么一会true一会false
先看图
你可能会觉得诧异,第一个打印不应该是undefined吗?a = 10默认不是 var a = 10吗?应该会提升到全局作用域的顶端的啊。那好,让我们把window.a打印出来看看
发现了吗。默认变量和函数差不多,块级作用域中默认声明的变量,只有执行了声明代码,变量才会被挂载到全局作用域上。
解答:结合上面的内容再看下面这个图
执行a = 10,我们知道,此时,在块级作用域中函数声明已经被提升到顶层,那么此时执行a = 10,就是相当于将函数声明a赋值为10!可以理解吗?然后,执行到函数声明语句,之前说过,块级内函数只有执行到声明的时候才会提升到全局去,此时,虽然这一行代码是函数声明语句,但是a,已经为数字10了,所以,执行function a(){}之后,a的值10就会被赋值给全局作用域上的a,所以下面打印的window.a,a都为10。块级作用域函数只有执行函数声明语句的时候,才会重写对应的全局作用域上的同名变量。这就解答了疑难点三,因为两个window a === a分别位于函数提升之前和函数提升之后,函数提升之后内外a相同,函数提升之前,内部a != 外部a,因为两个变量的值根本就不一样。所以疑难点二也就迎刃而解了,原题为什么最后输出的是4而不是5,因为a=5只赋值了内部的a,并没有被提升。所以这道题本质上是覆写→提升→覆写→提升的过程,因为最后一次提升之前的结果是4,所以外部输出为4,内部a被最后一次覆写为5。如果你想让外部也为5,我想你应该已经知道该怎么做了。