这是关于JS的怪异行为。
非严格模式
很久之前我在其它地方看见过类似下列的代码:
var a = 0;
{
a = 1;
function a() {}
a = 2;
console.log(a);
}
console.log(a);
这段代码在Chrome和Firefox下运行的结果是:
2
1
这段代码是反直觉的,说实话来研究JS的这种行为是没有多大意义的,但是知道为什么也是挺有趣的一件事。
其实上述代码执行结果是JS的实现和语言规范出现冲突导致的,经过我查询资料了解到(不一定准确),在ES5规范中,JS的块中严格不允许出现函数定义,但是浏览器在实现时却支持了这种写法,这就到导致在ES6有块级作用域时函数在其中的定义就会出很多奇怪的行为。
例如在上例代码在Safari中运行就会出现和恶Chrome不同的结果,不过我没有mac设备无法进行测试。
这段代码的关键字有:函数提升
,变量环境(VariableEnvironment)
,词法环境(LexicalEnvironment)
,所以我查询到下列资料:
- https://zhuanlan.zhihu.com/p/53135129
- https://stackoverflow.com/questions/31419897/what-are-the-precise-semantics-of-block-level-functions-in-es6)
得出一个结论,其实上列的代码实际上等同于:
var a1 = undefined;
{
let a2 = function a() {};
a2 = 1;
a1 = a2;
a2 = 2;
console.log(a2);
}
console.log(a1);
其中a1和a2都是a,区分出来只是为了方便理解,在块级作用域中定义的函数首先会提升到块顶部,在这里就是a2
,它实际上存在于词法环境
;同时也会提升到整段代码的顶部,在这里是a1
,它实际上存在于变量环境
,这是预编译阶段完成的。
接着在执行阶段,当代码执行到函数定义位置时,会进行一个赋值操作,内部a2
的值赋值到了外部a1
。
于是整个程序结束后输出的是2
和1
。
严格模式
上述操作只是在非严格模式下进行,当在严格模式下运行是结果似乎就符合逻辑了:
"use strict";
var a = 0;
{
a = 1;
function a() {}
a = 2;
console.log(a);
}
console.log(a);
代码输出
2
0
var替换为let
把var替换为let,那么结果和严格模式下的结果相同:
let a = 0;
{
a = 1;
function a() {}
a = 2;
console.log(a);
}
console.log(a);
代码输出
2
0
可能是因为ES6是默认严格模式原因
语言规范
当然最合适的是去查询语言规范,这里我给出相关的链接: https://262.ecma-international.org/6.0/#sec-block-level-function-declarations-web-legacy-compatibility-semantics 这段语言规范中描述了与本文相关的一些内容,目前在中文互联网中我没有找到相关的翻译,希望有小伙伴可以完成这个第一。
上诉内容只是我个人的想法与推测,毕竟预编译这个过程是黑箱操作,我现在还没有这个实例去查询相关的实现。
0 评论