detachEvent не работает с именованными встроенными функциями - PullRequest
2 голосов
/ 31 марта 2010

Сегодня я столкнулся с проблемой в 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 Ответ

10 голосов
/ 31 марта 2010

Разве первый фрагмент не должен работать нормально?

Да, возможно, так и должно быть. Но это не так. :-) К счастью, есть простой обходной путь (и лучше, чем arguments.callee, который имеет проблемы [см. Ниже]).

проблема

Проблема в том, что выражения именованных функций (NFE, которые у вас есть) не работают правильно в JScript (IE) или в некоторых других реализациях в дикой природе. Юрий Зайцев ( kangax ) провел тщательное исследование NFE и написал эту полезную статью о них.

Выражение именованной функции - это то, где вы присваиваете функции имя и используете оператор функции в качестве правого значения (например, правую часть присваивания или передаете его в функцию вроде attachEvent), вот так:

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

Это выражение функции, и функция названа. Возможно, это должно работать, но во многих реализациях в дикой природе, включая IE JScript, это не так. Именованные функции работают, и работают выражения анонимных функций, но не именованные функциональные выражения. ( Редактировать Я не должен был говорить не работает , потому что в некоторых отношениях они работают. Я должен был сказать, что не работает должным образом ; больше в Статья Юрия и мой ответ на ваш дополнительный вопрос .)

Решение

Вместо этого вы должны сделать это:

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 (и «строгий режим» - это, в основном, хорошая вещь).

...