块状作用域中声明函数分析

410 阅读3分钟

ECMAScript 6 入门书中总结了在ES6浏览器中的块状作用域声明函数的规律:

  • 1、允许在块级作用域内声明函数。
  • 2、函数声明类似于var,即会提升到全局作用域或函数作用域的头部。
  • 3、同时,函数声明还会提升到所在的块级作用域的头部。
var a = 0;
{
    console.log(a);
    a = 1;
    function a() {};
    a = 2;
    console.log(a);
}
console.log(a);
// 输出 [Function: a],2,1

分析:

1、首先分析第一个输出,根据上述的规律3得知函数声明会提升到所在块级作用域的头部,所以输出 [Function: a]

2、第二个输出,打印前赋值为2,所以输出2

3、第三个输出?

菜鸡的我首先想到的是0,简单粗暴的认为在块级作用域相当于是重新声明了一个变量a,与外边的变量a无关,所以输出0,但现实却是啪啪打脸。

猜测1:

正确的输出是1,而代码中确实有a=1的赋值操作,难道这里取到的是全局下的a吗?

var a = 0;
{
    console.log(a);
    console.log(window.a);
    a = 1;
    console.log(window.a);
    function a() {};
    a = 2;
    console.log(a);
}
console.log(a);
// 输出 [Function: a],0, 0, 2,1

看到这不淡定了,再一次被现实打脸,代码中唯一的一次赋值为1的操作都不能解释???

猜测2:

var a = 0;
{
    console.log(a);
    console.log(window.a);
    a = 1;
    console.log(window.a);
    function a() {};
    console.log(window.a);
    a = 2;
    console.log(window.a);
    console.log(a);
}
console.log(a);
// 输出 [Function: a],0, 0, 1, 1, 2,1

看到好像发现了不一样的地方,执行完 function a() {};这条语句后好像看到了想要的结果。

最后 个人猜测在块级作用域执行函数声明语句的时候会拿当前块级作用域的a赋值给外层的a,并且只有这一条语句会改变外层的a

var b = 0;
{
    console.log(a);
    a = 1;
    function a() {};
    a = 2;
    console.log(a);
}
console.log(b, window.a);
// [Function: a], 2, 0 1

最后结合开始说到的规律来分析下上边代码的执行:

  • 1、声明阶段
    • 1、扫描代码中的varfunction关键字
    • 2、根绝规律2分析得知,在全局下声明变量a和变量b
  • 2、执行阶段
    • 1、给b赋值0
    • 2、根据规律3的得知函数声明会提升到所在的块级作用域的头部,所以这里打印 [Function: a],这里打印的是块级作用域中的a
    • 3、给块级作用域中的a赋值为1
    • 4、关键来了,由上边的分析知道这里会拿当前块级作用域的a赋值给外层的a给外层作用域下的a,伪代码:window.a = a
    • 5、给块级作用域中的a赋值为2
    • 6、输出块级作用域中的a,此时a的值为2
    • 7、输出全局作用域下的bab没有动过,所以直接输出0,a由于上边已经修改为1,所以输出1。

目前测试结果:

chrome: [Function: a], 2, 0 1
firefox: [Function: a], 2, 0 1
safari: [Function: a], 2, 0 2 // safari 中并没有受到块级作用域的影响
edge: [Function: a], 2, 0 undefined // edge 中对 a 的声明并没有提升到全局作用域
QQ浏览器: [Function: a], 2, 0 1
IE11: [Function: a], 2, 0  [Function: a] // IE中会提升 a 到全局作用域下,但也仅仅只有这一步,没有其他的操作

参考

块级作用域