块级作用域问题

我理解这应该是一个块级作用域的问题,代码如下:

var a = 0;
console.log(1, a)
if (true) {
  console.log(2, a);
  a = 1;
  console.log(3, a);
  function a() {}
  console.log(4, a);
  a = 21;
  console.log(5, a);
}
console.log(6, a);

输出的结果是
image.png

主要是这个 6, 1 我很难理解...

阅读 1.8k
2 个回答

整体的运行流程如下:

var global.a = 0;
console.log(1, global.a) // 1 0
if (true) {
  var block.a = function a() {}
  console.log(2, block.a); // 2 ƒ a() {}
  global.a = block.a = 1;
  console.log(3, block.a); // 3 1
  console.log(4, block.a); // 4 1
  block.a = 21;
  console.log(5, block.a); // 5 21
}
console.log(6, global.a); // 6 1

至于block.a = 21的时候,global.a还是1, 就不明白是啥机制了, 等大神。

主要是条件中的函数声明,这个是不确定的行为,最好不要这么搞
https://developer.mozilla.org...

为什么最后是等于1从代码层面很难分析出来,我们通过字节码来一探究竟


StackCheck 
LdaZero #常量0
Star r1 #保存至r1 —— global.a
LdaGlobal [0], [0] #window.console
Star r3 #保存至r3
LdaNamedProperty r3, [1], [2] #console.log
Star r2 #保存至r2
LdaSmi [1] #常量1
Star r4 #保存r4
CallProperty2 r2, r3, r4, r1, [4] #window.console.log(r4[=1],r1[=0])
CreateClosure [2], [6], #创建闭包function a
Star r0 #r0=function a
LdaGlobal [0], [0]  
Star r3 
LdaNamedProperty r3, [1], [7] 
Star r2 
LdaSmi [2] #常量2
Star r4 
CallProperty2 r2, r3, r4, r0, [9] #window.console.log(r4[=2],r0[=fun]])
LdaSmi [1] #常量1
Star r0 #r0=1
LdaGlobal [0], [0]
Star r3
LdaNamedProperty r3, [1], [11] 
Star r2
LdaSmi [3] #常量3
Star r4
CallProperty2 r2, r3, r4, r0, [13] #window.console.log(r4[=3],r0[=1])
Mov r0, r1 # r0=>r1, 相当于r1 = r0,即global.a = block.a = 1
LdaGlobal [0], [0]
Star r3
LdaNamedProperty r3, [1], [15]
Star r2
LdaSmi [4] #常量4
Star r4
CallProperty2 r2, r3, r4, r1, [17] #window.console.log(r4[=4],r1[=1])
LdaSmi [21] #常量21
Star r0
LdaGlobal [0], [0]
Star r3
LdaNamedProperty r3, [1], [19]
Star r2
LdaSmi [5] #常量5
Star r4
CallProperty2 r2, r3, r4, r0, [21] #window.console.log(r4[=5],r0[=21])
LdaGlobal [0], [0]
Star r3
LdaNamedProperty r3, [1], [23]
Star r2
LdaSmi [6] #常量6
Star r4
CallProperty2 r2, r3, r4, r1, [25] #window.console.log(r4[=6],r1[=1])
LdaUndefined 
Return 

这是通过node编译出的字节码,#号后面是我标注的注释,重点在于Mov r0,r1这个指令,也就是在console.log(3, a);之后,引擎额外执行了这条语句(从代码层面看不出);故而最后再打印r1的值为1;
如果你开启严格模式,最后输出就会是0,比较符合预期,而严格模式下字节码中并没有Mov这条指令,而正常模式下Edge的输出则不同于chrome,非严格模式下引擎之间会有自己的优化算法,同一段代码可能输出不一样,尤其像这种不规范的代码

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题