detachEvent不使用命名内联函数

时间:2010-03-31 14:12:19

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

我今天在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"
});

给出了什么?!第一个代码段不应该正常工作吗?

1 个答案:

答案 0 :(得分:10)

  

第一个代码段不应该正常工作吗?

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

问题

问题在于命名函数表达式(NFE,这就是你所拥有的)在JScript(IE)或其他几种实现中无法正常工作。 Yuriy Zaytsev(kangax)对NFE进行了彻底调查,并撰写了this useful article关于他们的信息。

命名函数表达式是指为函数指定名称​​和使用函数语句作为右侧值(例如,赋值的右侧部分,或将其传递给函数如attachEvent),如下所示:

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

这是一个函数表达式,并且该函数已命名。可以说它应该工作,但在野外的许多实现中,包括IE的JScript,它没有。命名函数可以工作,匿名函数表达式可以工作,但不能命名函数表达式。 (编辑我不应该说不能正常工作,因为在某些方面他们会这样做。我应该说正常 ;更多在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的新“严格模式”中也是不允许的(“严格模式”主要是一件好事。)