>Форум Mozilla Россия http://forum.mozilla-russia.org/index.php >Разработка http://forum.mozilla-russia.org/viewforum.php?id=18 >Firefox 4. Механизм работы removeEvenListener и bind() http://forum.mozilla-russia.org/viewtopic.php?id=48291 |
hydrolizer > 22-02-2011 14:53:07 |
Сразу оговорюсь, что всё нижеприведенное относится именно к Fx4, т.к. использует имплементацию bind() стандарта JavaScript 1.8.5 (в более ранних версиях Fx bind реализовывался "вручную"; как всё ниженаписанное могло бы выглядеть при такой реализации - не проверял). Все примеры максимально упрощены для приведения здесь, но в реальной ситуации картина абсолютно та же самая. Выделить код Код:<vbox> <button id="btn1" label="1" oncommand="doSubscribeUnsubscribe(true)"/> <button id="btn2" label="2" oncommand="doSubscribeUnsubscribe(false)"/> <button id="btn3" label="3"/> </vbox> и код к ней: Выделить код Код:Components.utils.import("resource://gre/modules/Services.jsm"); function doSubscribeUnsubscribe(flag) { try { var btn=document.getElementById("btn3"); if (flag) btn.addEventListener("command",some.something.doSome,false); else btn.removeEventListener("command",some.something.doSome,false); Services.console.logStringMessage("doSubscribeUnsubscribe: done"); } catch(err) { Services.console.logStringMessage("doSubscribeUnsubscribe: "+err); } }; var some={}; some.something= { thing: "foo", doSome: function() { alert(this.thing); } } Разумеется, в данном случае при нажатии на btn3 мы получим алерт с содержимым undefined - контекст исполнения принадлежит кнопке (в этом можно легко убедиться, сделав, например, alert(this.thing+": "+this)). Однако нам в вызываемом методе нужен именно контекст объекта, который этот метод содержит, поэтому делаем так: - вроде бы всё в порядке, получаем алерт с содержимым foo. Теперь нам кнопку надо отписать от этого метода - т.е. жмем кнопку 2 - и ничего не происходит. Меняем код отписки с вышеприведенного на вот такой: Выделить код Код:btn.removeEventListener("command",some.something.doSome.bind(some.something),false); - толку ноль. Изменяем метод doSome вот таким образом: и убираем bind(...) в подписке - всё в порядке, кнопка нормально подписывается на обработчик, и нормально от него отписывается. Видимо, дело заключается именно в наличии bind(). На всякий случай проверяю документацию:
Поскольку сигнатура removeEventListener такова: и с первым и третьим аргументом ошибиться сложно, остается второй - передаваемая ссылка на listener. Опять же, из документации:
- именно так и поступаю, ссылка есть - листенер не удаляется. Единственное предположение заключается в следующем: если в метод, в котором навешивается/снимается подписка, вставить: Выделить код Код:Services.console.logStringMessage("with bind:\r\n"+some.something.doSome.bind(some.something).toSource()); Services.console.logStringMessage("without bind:\r\n"+some.something.doSome.toSource()); то получим соответственно
- т.е. в случае с bind() скриптовой движок "не видит" реализации метода листенера - может быть, поэтому он не может по ссылке идентифицировать листенер? Это только моё предположение, и если кто-то разъяснит, как указанную ситуаций обойти - буду премного благодарен, т.к. в реальном коде очень много чего завязано на контекст выполнения методов обработчиков, и переписывать их все на статический контекст было бы не особенно интересно. |
hydrolizer > 22-02-2011 17:24:04 |
Мда.. то ли авторы документации не совсем внятно выразились, то ли я не до конца понял вышепроцитированное вот это:
В общем, проблема решается так: Выделить код Код:Components.utils.import("resource://gre/modules/Services.jsm"); function doSubscribeUnsubscribe(flag) { try { var btn=document.getElementById("btn3"); if (flag) btn.addEventListener("command",fWrapper,false); else btn.removeEventListener("command",fWrapper,false); Services.console.logStringMessage("doSubscribeUnsubscribe: done"); } catch(err) { Services.console.logStringMessage("doSubscribeUnsubscribe: "+err); } }; var some={}; some.something= { thing: "foo", doSome: function() { alert(this.thing); } } var fWrapper=some.something.doSome.bind(some.something); - т.е. надо держать ссылку не на просто метод метод подписки, а ссылку на метод, уже привязанный к нужному контексту - биндинг контекста непосредственно в вызове addEventListener дает нам на выходе анонимный метод. Всем спасибо |
Infocatcher > 22-02-2011 18:44:34 |
Еще можно сделать объект с методом handleEvent и передавать его в addEventListener/removeEventListener вместо функции, тогда this будет ссылаться на этот объект. |
hydrolizer > 23-02-2011 03:22:07 |
Infocatcher |