本文章将从一个问题上引出:问题如下图:
大家自己先分析一下,运用javaScript的预编译,作用域等知识来分析,是不是应该打印出来如下答案:
1 0
2 0
但是打印出来的结果是
1 0
2 5
我当时也纳闷了,怀疑自己的基础知识不扎实(其实也还说的过去吧)
那么,下面我们围绕上面给出的问题来引出下面两个问题:
1.块级作用域和函数声明间发生了什么事情。
2.浏览器在实现ES规范上是怎么处理的。
针对上面的问题,我用sublime text终端,谷歌(版本 78.0.3904.108(正式版本) (64 位)), 火狐(70.0.1 (64 位)),ie11, ie10 以及 ie9打印结果如下:
我们会发现,
1.ie打印出来的结果相比较于终端, 谷歌以及火狐的结果不一样。
2.ie11 相比较于 ie10和ie9的结果也不一样。
3.终端,谷歌和火狐打印出来的是 :
5 0
6 5
4.ie10,ie9 打印出来的是:
5 0
6 0
我们来分析一下第四点:
打印出来的结果是符合我们运用预编译,作用域等知识分析出来的结果的。
在结合第三点分析:
大家看一下我标注的谷歌和火狐的版本号,版本基本上是我在写这篇文章时是最新的了,下面会用到。
所以,我们可以简单的推断一下,我目前使用的谷歌和火狐浏览器里面已经实现了es6规范了。但是ie9, ie10浏览器里面是没有实现es6规范,所以打印出来的结果是符合我们运用预编译,作用域等知识分析出来的结果的。
所以,关于文章最开头抛出的问题的答案,0,5和0, 0都是没有错误的,但是打印哪一个结果就得看浏览器版本了(实际上是看浏览器上是不是实现了es6规范)。
大家肯定会进行追问,那么浏览器是怎么打印出 0, 5这个结果的呢?
这里,还得借用一下阮一峰老师的《ES6标准入门》第三版里面的部分知识帮助我们理解(如果本次引用此书有侵权行为,麻烦及时联系我,我马上删除本文章,谢谢)。
书上内容如下图(请大家仔细阅读):
麻烦各位一定要看完上面的内容,在继续往下学习。谢谢。
书上有几个比较重要的点,我在下面列举出来,值得大家注意一下:
1.ES5规定:函数只能在顶层作用域和函数作用域之中声明,不能再块级作用域声明。但是,浏览器并没有遵守这个规定,为了兼容以前的旧代码,还是支持再块级作用域之中声明函数。
2.ES6规定:在块级作用域之中,函数声明语句的行为类似于let,在块级作用域之外不可引用。但是,为了兼容以前的旧代码,ES6在附录B中规定,浏览器的实现可以不遵守上面的规则,而有自己的行为方式,具体如下:
1.允许在块级作用域内声明函数
2.函数声明类似于var, 即会提升到全局作用域或者函数作用域的头部
3.同时,函数声明还会提升到所在块级作用域的头部。
接下来,我们在通过上面的规则来分析一下题目:
经过分析,大家应该还是得到0 0这一个答案。
那么我们按下面代码打印一下题目,代码和结果如下图:
下面为大家分析一下:(前提大家要区分好预编译和代码执行)
首先,我这个版本的浏览器已经实现了es6规范。有一个全局变量a, 有个块级作用域if(true) {}, 块级作用域里面有函数声明,根据上述规则,会在全局作用域的头部var a = undefined; 然后函数声明会提上到块级作用域的头部。
根据打印的结果(第181行),然后执行代码if(true) {},执行块级作用域代码,执行赋值操作和打印变量的操作。我们会发现在执行函数声明这个语句(第184行)之后,window.a(全局的变量a)就等于5(第185行), 然后执行 a = 0, a变成0,但是window.a还是为5。
所以,我们就可以推断出如下规则:
在函数执行的前一时刻(即函数未真正执行,是预编译过程),会发生如下操作:
1.{}/if() {}本来是没有块级作用域的,一旦{}中有了函数声明,就会形成块级作用域,而且会创建块级变量a。如果不使用window.a只会操作块级变量a.同时,函数声明类似于var, 即会提升到全局作用域或者函数作用域的头部;函数声明还会提升到所在块级作用域的头部。
函数执行时:
1.当代码执行到函数声明时,会将块级变量a当前的值赋给最接近块级作用域的函数作用域或者是全局作用域的同名的变量(即某函数作用域下的a, 或者是window.a)
2.块级代码执行完毕后,销毁块级作用域
下面,我们通过一段代码证明我们推导出来的规则是正确的:
下面的代码中最接近块级作用域是函数作用域(有一个立即执行函数包裹)
var b = 199
{
b = 100
if(true) {
(function() {
var b = 222;
console.log(window.b, b)
if(true) {
console.log(window.b, b)
b = 23
console.log(window.b, b)
function b() {}
console.log(window.b, b)
b = 333;
function b() {}
console.log(window.b, b)
}
console.log(window.b, b);
}());
}
console.log(window.b, b)
}
console.log(window.b, b)
下面的代码中最接近块级作用域是全局作用域
var b = 199
{
b = 100
if(true) {
console.log(window.b, b)
if(true) {
console.log(window.b, b)
b = 23
console.log(window.b, b)
function b() {}
console.log(window.b, b)
b = 333;
function b() {}
console.log(window.b, b)
}
console.log(window.b, b);
}
console.log(window.b, b)
}
console.log(window.b, b)
打印结果如下:
最后,大家要注意的是,在开发中要尽量避免不要在块级作用域中声明函数,这不符合开发规范。
到这里,就是关于ES6的块级作用域与函数声明的所有内容了。
上面的部分知识引用了阮一峰老师的著作《ES6标准入门》第三版,如果该部分有侵权行为,麻烦及时联系我,我马上删除文章。
如果转载文章,麻烦备注出处,谢谢!
最后,希望大家对这篇文章里面的内容有什么不了解或者文章有什么错误的地方,麻烦在评论区中指出,不吝赐教。希望分享这篇文章可以帮助到大家,感谢各位的阅读,我们一起进步,一起成长,一起探索世界。