【问题标题】:detachEvent not working with named inline functionsdetachEvent 不适用于命名的内联函数
【发布时间】:2021-04-28 22:03:28
【问题描述】:

我今天在 IE8 中遇到了一个我似乎无法解释的问题(注意我只需要支持 IE):detachEvent 在使用命名匿名函数处理程序时不起作用。

document.getElementById('iframeid').attachEvent("onreadystatechange", function onIframeReadyStateChange() {
    if (event.srcElement.readyState != "complete") { return; }
    
    event.srcElement.detachEvent("onreadystatechange", onIframeReadyStateChange); 

    // code here was running every time my iframe's readyState 
    // changed to "complete" instead of only the first time
});

我最终发现将 onIframeReadyStateChange 更改为使用 arguments.callee(我通常避免这样做)反而解决了这个问题:

document.getElementById('iframeid').attachEvent("onreadystatechange", function () {
    if (event.srcElement.readyState != "complete") { return; }
    
    event.srcElement.detachEvent("onreadystatechange", arguments.callee);    
    
    // code here now runs only once no matter how many times the 
    // iframe's readyState changes to "complete"
});

什么给了?!第一个 sn-p 不应该工作正常吗?

【问题讨论】:

  • 冒昧更改标题,“命名匿名函数”是矛盾的。 :-)

标签: javascript internet-explorer dom-events anonymous-function


【解决方案1】:

第一个 sn-p 不应该正常工作吗?

是的,可以说应该。但事实并非如此。 :-) 幸运的是,有一个简单的解决方法(并且比 arguments.callee 更好,后者存在问题 [见下文])。

问题

问题是命名函数表达式(NFE,就是你所拥有的)在 JScript (IE) 或其他几个野外实现中不能正常工作。 Yuriy Zaytsev (kangax) 对 NFE 进行了彻底的调查,并在this useful article 上写了关于它们的文章。

命名函数表达式是您给函数一个名称​​并使用函数语句作为右手值(例如,赋值的右手部分,或将其传递给函数如attachEvent),像这样:

var x = function foo() { /* ... */ };

那是一个函数表达式,函数被命名。可以说它应该可以工作,但在许多野外实现中,包括 IE 的 JScript,它却不行。命名函数有效,匿名函数表达式有效,但 named 函数表达式无效。 (编辑我不应该说不工作,因为在某些方面他们会这样做。我应该说不工作正常 ;更多内容请参见 Yuriy 的文章和 my answer to your follow-up question。)

解决办法

相反,您必须这样做:

var x = foo;
function foo() { /* ... */ };

...毕竟,这确实是同一件事。

所以在你的情况下,只需这样做:

document.getElementById('iframeid').attachEvent("onreadystatechange", onIframeReadyStateChange);
function onIframeReadyStateChange() {
    if (event.srcElement.readyState != "complete") { return; }

    event.srcElement.detachEvent("onreadystatechange", onIframeReadyStateChange);

    // code here was running every time my iframe's readyState
    // changed to "complete" instead of only the first time
}

这与您尝试做的效果相同,但不会遇到实施问题。

arguments.callee 的问题

(这有点跑题了,但是...)避免使用arguments.callee 是对的。在大多数实现中,使用它会带来大量 性能开销,从而将函数调用减慢一个数量级(是的,真的;不,我不知道为什么)。在 ECMAScript 5 的新“严格模式”中也不允许这样做(“严格模式”主要是一件好事)。

【讨论】:

  • 你知道有哪些关于访问arguments.callee的性能开销的好文章吗?
  • @Roatin:不,我也想看一个。也许我会写一个。当我向 Prototype 团队 (groups.google.com/group/prototype-core/browse_thread/thread/…) 提出一种更有效的超级调用方法时,kangax 向我指出它很慢,当我对其进行测试时,我被震撼了。在我当时的测试中,Chrome 的开销最大,IE 最少。 (应该说最慢的 Chrome 比最快的 IE 快;只是 Chrome 的相对变化更大。)
  • @Roatin:还没有通读,但这似乎涵盖了它:webreflection.blogspot.com/2009/06/… 在我对原型的测试中,我发现了两层缓慢:第一层是如果你使用arguments (这并不奇怪,因为 arguments 是一个非常奇怪的东西——它是“活的”,一方面,如果你有一个命名参数并为其分配一个新值,它的条目在arguments 数组中自动更新;反之亦然),然后如果您实际使用了callee,则会出现第二次减速。
  • @T.J.:我在stackoverflow.com/questions/2679657/…发布了一个后续问题
猜你喜欢
  • 2015-11-06
  • 1970-01-01
  • 2021-08-12
  • 2013-11-15
  • 2016-06-19
  • 2015-08-13
  • 2018-11-07
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多