JavaScript - 在 Safari 上执行代码之前可用的对象定义

2024-01-09

我只需要在页面加载时执行一次的对象和函数被包装在undefined检查物体。在我通常使用的 Windows/Linux 上的 Chrome 上,代码运行得很好,即代码只执行一次。但在 iPad 和 MacBook 上的 Safari 上,未定义的检查不起作用,即根据浏览器,对象/函数已经被声明,甚至代码执行还没有到达那里!

我已经简化了我的代码,只包含一个if循环检查嵌套函数是否已声明。由于它不应该在第一次声明,所以我已经包括了someVariable在 - 的里面if那不应该是undefined.

在 Chrome 和 Safari 上运行相同的函数并查看差异。


if (typeof anObject == 'undefined') {

    function anObject(someParameter = 'someParameter') {
        var someProperty = 'someProperty';

        function someMethod(someParameter) {
            console.log(someParameter);
        }
    }

    console.log('Hi');
    var someVariable = 404;
}

在 Chrome 上,您可以看到控制台日志记录'Hi'还有someVariable为 404。但在 Safari 上,没有控制台日志记录,并且 someVariable 未定义。

如果您设置断点来了解发生了什么 - 第一个未定义的检查实际上永远不会起作用。这anObject甚至在声明之前就已定义。

我尝试搜索 V8(Chrome JS 引擎)和 JavaScriptCore(Safari 引擎)之间的差异,但没有找到任何可靠的东西。我认为这与执行和功能提升有关。如果有人能向我解释执行中出现这种差异的原因,那就更好了。 iPad 上的行为是相同的,甚至在 Chrome 上也是如此!

Updates:

  1. 我发现了关于不同执行的类似问题。似乎 就像这是与函数提升相关的东西,但不能 尚未找到任何可靠的来源。Chrome 和 Firefox 中的 Javascript 提升 https://stackoverflow.com/questions/14242399/javascript-hoisting-in-chrome-and-firefox

  2. 现在看来,实际上是一种吊装行为。这是通过使用函数表达式来实现的。在这种情况下,只需简单替换即可function anObject() with var anObject = function()。我认为,通过这样做,即使函数在执行之前被提升和评估,变量也不会被分配函数引用。

  3. 根据 PhistucK 的建议,我已经在 WebKit 问题跟踪器上打开了该问题(Bug#199823 https://bugs.webkit.org/show_bug.cgi?id=199823), 铬讨论 https://groups.google.com/a/chromium.org/forum/#!topic/chromium-discuss/UWDmDK4uKak和 TC39 ECMA262 Github(问题#1632 https://github.com/tc39/ecma262/issues/1632).

  4. 这是 2016 年报告的现有 Webkit 错误 -错误 163209 - [ES6]。在全局范围内实施附件 B.3.3 函数提升规则 https://bugs.webkit.org/show_bug.cgi?id=163209. 我现在在我的回答中总结了研究.


此行为与在 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.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

JavaScript - 在 Safari 上执行代码之前可用的对象定义 的相关文章

随机推荐