detachEvent不使用命名内联函数

Pol*_*ant 2 javascript internet-explorer anonymous-function javascript-events

我今天在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
});
Run Code Online (Sandbox Code Playgroud)

我最终发现改变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"
});
Run Code Online (Sandbox Code Playgroud)

是什么赋予了?!第一个代码段不应该正常工作吗?

T.J*_*der 10

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

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

问题

问题是命名函数表达式(NFE,这是你在那里)在JScript(IE)或其他几个实现中无法正常工作.Yuriy Zaytsev(kangax)对NFE进行了彻底的调查,并撰写了这篇有用的文章.

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

var x = function foo() { /* ... */ };
Run Code Online (Sandbox Code Playgroud)

这是一个函数表达式,函数名为.可以说它应该可以工作,但是在野外的许多实现中,包括IE的JScript,它都没有.命名函数可以工作,匿名函数表达式可以工作,但不能命名函数表达式.(编辑我不应该说不工作,因为在某些方面他们这样做.我应该说不能正常工作 ;更多在Yuriy的文章和我对你的后续问题的回答.)

解决方案

相反,你必须这样做:

var x = foo;
function foo() { /* ... */ };
Run Code Online (Sandbox Code Playgroud)

...毕竟,它确实来了同样的事情.

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

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
}
Run Code Online (Sandbox Code Playgroud)

这与您尝试执行的操作具有相同的效果,但不会遇到实现问题.

这个问题 arguments.callee

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

  • 这怎么没有任何投票?很棒的答案. (2认同)