var a = 0;
if (true) {
console.log(a)
a = 1;
function a() {}
a = 21
console.log(a)
}
console.log(a)
Run Code Online (Sandbox Code Playgroud)
在我看来,因为函数声明提升,a = 1
以及 a = 21
会改变局部函数变量,所以在块中会输出21,外面是0,但真正的结果是在输出1之外。
用chrome调试,结果是这样
当运行时function a() {}
,它会改变局部和全局变量。太奇怪了?谁能给我解释一下?
Ben*_*Ben 13
观察到的行为是非严格模式特有的,Firefox 也做同样的事情。
之所以会这样,是因为它遵循了 Web 兼容性语义,如附件 B 3.3 中所述规范中所述。
细节非常复杂,但这里是引擎这部分的作者实现的:
当
a
块中存在内部函数时,处于草率模式,并且应用 Web 兼容性语义时(规范中描述的场景),则:
- 内部函数
a
在let
块内用-like 块作用域提升("(let) a
")- 同时,在包含块 (" ") 的作用域中创建了一个变量,同样具有 name
a
,但具有var
语义(即函数作用域(var) a
)- 当到达声明内部函数的行时,将的当前值
(let) a
复制到(var) a
(!!)a
块内部对 name 的后续引用将引用(let) a
因此,对于以下情况:
1: var a = 0
2: if(true) {
3: a = 1
4: function a() {}
5: a = 2
6: }
7: console.log(a)
Run Code Online (Sandbox Code Playgroud)
...这是发生的事情:
第 1 行: (var) a
添加外部作用域的变量环境,并0
赋值给它
第 2 行: 创建一个 if 块,(let) a
(即function a() {}
)被提升到块的顶部,阴影(var) a
第 3 行: 1
分配给(let) a
(注意,不是(var) a
)
第 4 行: 的值(let) a
被复制到 中(var) a
,因此(var) a
变为1
第 5 行: 2
分配给(let) a
(注意,不是(var) a
)
第 7 行: (var) a
打印到控制台(所以1
打印)
Web 兼容性语义是一组行为,这些行为尝试对回退语义进行编码,以使现代浏览器能够尽可能多地保持与 Web 上遗留代码的向后兼容性。这意味着它对规范之外实现的行为进行编码,并且由不同的供应商独立实现。在非严格模式下,由于浏览器供应商“走自己的路”的历史,几乎可以预料到奇怪的行为。
但是请注意,规范中定义的行为可能是错误传达的结果。Allen Wirfs-Brock在推特上说:
无论如何,根据我想象的任何合理解释,最后报告的...结果 a==1 都不正确。
...和:
应该是
function a(){}
!因为在块内,所有a
对块级绑定的显式引用。只有隐含的 B.3.3 赋值应该去外部a
最后请注意:Safari浏览器,给出了一个完全不同的结果(0
由第一印刷console.log
,并且21
通过最终印刷console.log
)。据我了解,这是因为 Safari 还没有对 Web 兼容性语义进行编码(例如)。
故事的道德启示?使用严格模式。
归档时间: |
|
查看次数: |
962 次 |
最近记录: |