此行为与在 Webkit 引擎中使用草率模式有关,有一个错误 https://bugs.webkit.org/show_bug.cgi?id=163209。让我总结一下研究:
具体来说,该示例有三个关键方面:非严格模式代码,一个函数是在块内声明 and 在该块之前引用.
正如介绍附录B.3.3 https://tc39.es/ecma262/#sec-block-level-function-declarations-web-legacy-compatibility-semantics解释说,块语句内的函数声明最初并不是语言规范的一部分;这是浏览器经常实现的扩展,每个浏览器都以自己独特的方式实现。 ES2015 试图尽可能多地指定这种行为,但由于浏览器之间的差异无法完全协调,一些现有代码仍然不可避免地无法移植。
“以下是我们被迫指定的内容,因为网络浏览器
实现了这个行为,然后页面开始依赖它,但是我们
对此并不满意。” - 附件 B 3.3
In 草率模式,JavaScriptCore 的行为确实与正常行为不同:
λ eshost -sx "if (typeof foo === 'undefined') { function foo() {} print('ok'); } else { print('hmm'); }"
#### ch, sm, v8, xs
ok
#### jsc
hmm
一种解决方案是使用“严格”模式:
λ eshost -sx "(function () { 'use strict'; if (typeof foo === 'undefined') { function foo() {} print('ok'); } else { print('hmm'); } })()"
#### ch, jsc, sm, v8, xs
ok
而且,这显然只发生在 Safari 的脚本顶层。在函数中,如
function g(){
console.log(typeof f);
{
function f(){}
}
}
g();
Safari 符合规范。这很可能是因为脚本顶层的行为仅在 ES2016 中指定,8582e81 https://github.com/tc39/ecma262/commit/8582e812cd7f6d4aa15890e2a65800fe4785e8c7,与 ES2015 中指定的函数中的行为相反。
来源:评论者发表的评论罗斯·科斯林 https://github.com/rkirsling and 凯文·吉本斯 https://github.com/bakkot在 GitHub 问题上#1632 https://github.com/tc39/ecma262/issues/1632.
2016 年报告了一个与此提升行为相关的现有错误,Webkit 问题#16309:[ES6]。在全局范围内实施附件 B.3.3 函数提升规则 https://bugs.webkit.org/show_bug.cgi?id=163209。这是一个Test262 https://test262.report/browse/annexB/language/global-code/block-decl-global-existing-block-fn-no-init.js涵盖这一点的案例。
为了解决这个问题,我使用了函数表达式:
这是我替换的function anObject()
with var anObject() = function()
。现在运行此代码以了解流程:
if (typeof anObject == 'undefined') {
if (typeof anObject == 'undefined') console.log('anObject not defined inside block')
if (typeof someVariable == 'undefined') console.log('someVariable not defined as of now');
var anObject = function(someParameter = 'someParameter') {
var someProperty = 'someProperty';
}
console.log('anObject is now defined');
var someVariable = 404;
if (typeof someVariable == 'undefined') console.log('someVariable not defined as of now');
}
这里发生了什么事?
函数和变量被提升到顶层。但像 V8 (Chrome) 这样的引擎会在代码执行期间从语义上定义函数名称。然而,在Webkit浏览器的草率模式下,即使在ECMA2015/16标准化之后,函数名称也是在执行之前定义的。请注意,在两个引擎上,函数实际上是在任何内容之前定义(提升)的 - 这只是关于函数的语义name。上面的代码在执行期间将匿名函数的引用(因为它现在没有名称)分配给 anObject,这在 Safari 上也可以正常运行。关于块作用域和提升的一个很好的解释ES6 中块级函数的精确语义是什么? https://stackoverflow.com/questions/31419897/what-are-the-precise-semantics-of-block-level-functions-in-es6.