我只需要在页面加载时执行一次的对象和函数就封装在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上的行为相同!
更新:
我发现了关于不同执行方式的类似问题。好像 像这样与功能提升有关,但不能 找到任何可靠的来源。 Javascript Hoisting in Chrome And Firefox
看起来现在实际上是一种起吊行为。这可以通过使用函数表达式来实现。在这种情况下,只需将function anObject()
替换为var anObject = function()
。通过这样做,我认为,即使在执行之前提升和评估函数,也不会为变量分配函数引用。
按照PhistucK的建议,我已在WebKit问题跟踪器(错误#199823,Chromium Discuss和TC39 ECMA262 Github(问题#1632)上打开了该问题。
这是2016年报告的现有Webkit错误- Bug 163209 - [ES6]. Implement Annex B.3.3 function hoisting rules for global scope 。 我现在在我的答案中总结了研究。
答案 0 :(得分:0)
您找到的另一个SO问题实际上回答了这个问题。简而言之:
在条件语句中声明函数是非标准的,因此请勿这样做。
鉴于它是非标准的,浏览器可以自由地采取不同的行为。
此外,我认为您的技术不会带来任何好处。只需取消“ if undefined”检查,然后无条件定义顶级功能即可。无论如何,它们只会分配一次。
答案 1 :(得分:0)
此行为与在Webkit引擎which has a bug中使用草率模式有关。让我结束研究:
具体来说,该示例包含三个关键方面:在非严格模式代码中,在一个块内声明一个函数,并在该函数之前引用封锁。
正如Annex B.3.3的介绍所解释的那样,块语句中的函数声明最初并不是语言规范的一部分;这是浏览器经常实施的扩展,每种扩展都有自己独特的方式。 ES2015试图尽可能多地指定这种行为,但是由于浏览器之间的差异无法完全协调,因此某些现有代码不可避免地不可移植。
“这是我们由于网络浏览器而不得不指定的内容 实施此行为,然后页面开始依赖它,但是我们 不满意。”-附件B 3.3
在马虎模式中,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中指定,而不是函数在ES2015中指定的行为。
来源:Ross Kirsling和Kevin Gibbons在GitHub问题#1632上发表的评论。
2016年报告了一个与该起吊行为有关的现有错误,即Webkit Issue #16309: [ES6]. Implement Annex B.3.3 function hoisting rules for global scope。这是一个Test262案例,涵盖了这一点。
为解决此问题,我使用了函数表达式:
也就是说,我将function anObject()
替换为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上也可以正常运行。关于块作用域和在What are the precise semantics of block-level functions in ES6?上吊起的很好的解释。