Передача this и аргумента функции addEventListener без использования bind
После удаления addEventListener в загрузочном аддоне, не работающем, когда аддон отключен, я изучаю другие возможности.
Помимо использования bind()
и кэшируя связанную функцию, есть ли способ использовать this и передать аргумент?
// works fine but can't pass argeement
contextMenu.addEventListener('popupshowing',
this.contextPopupShowing, false);
// passes the argument but 'this' is no longer available
contextMenu.addEventListener('popupshowing',
function(){this.contextPopupShowing(window);}, false);
Я использовал несколько слушателей событий с bind()
и я ищу альтернативные методы без использования bind()
Я даже пытался схватить window
с рекурсивной функцией из <menupopup id="contentAreaContextMenu" ...>
Обновить: bind()
мешает removeEventListener
4 ответа
Поскольку мы говорим о перезапускаемых надстройках... Многие перезапускаемые надстройки используют unload
а также unloadWindow
вспомогательные функции, облегчающие реализацию shutdown
правильно, а также помочь с такими вещами, как addEventListener
, так что потерпи немного.
Помощники - unload
Первый, unload
является вспомогательной функцией, которой вы передаете другую функцию, которая будет выполняться shutdown
(или можно вызвать вручную). Большинство реализаций очень похожи на это:
var unloaders = []; // Keeps track of unloader functions.
function unload(fn) {
if (typeof(fn) != "function") {
throw new Error("unloader is not a function");
}
unloaders.push(fn);
return function() {
try {
fn();
}
catch (ex) {
Cu.reportError("unloader threw " + fn.toSource());
Cu.reportError(ex);
}
unloaders = unloaders.filter(function(c) { return c != fn; });
};
}
Затем вы бы подключить shutdown
поступать правильно
function shutdown() {
...
for (let i = unloaders.length - 1; i >= 0; --i) {
try {
unloaders[i]();
}
catch (ex) {
Cu.reportError("unloader threw on shutdown " + fn.toSource());
Cu.reportError(ex);
}
}
unloaders.length = 0;
}
С помощью unload
Теперь вы можете делать такие вещи, как:
function startup() {
setupSomething();
unload(removeSomething);
setupSomethingElse();
var manualRemove = unload(removeSomethingElse);
...
if (condition) {
manualRemove();
}
}
Помощники - unloadWindow
Вы обычно хотите создать вторую функцию unloadWindow
выгружать вещи, когда ваше дополнение закрыто или окно закрыто, что бы ни случилось в первую очередь. Не убирать вещи, когда окно закрывается, может быть очень сложно, и создать зомби отсеки вашего bootstrap.js
и / или кодировать модули очень легко (это из опыта написания и просмотра перезапускаемых надстроек).
function unloadWindow(window, fn) {
let handler = unload(function() {
window.removeEventListener('unload', handler, false);
try {
fn();
}
catch (ex) {
Cu.reportError("window unloader threw " + fn.toSource());
Cu.reportError(ex);
}
});
window.addEventListener('unload', handler, false);
};
(Некоторые люди могут хотеть "оптимизировать" это, чтобы иметь только один "unload"
обработчик, но обычно у вас есть только так unloadWindow
называет, что это не имеет значения.)
Собираем все вместе
Теперь вы можете .bind
и все, что угодно, и пусть закрывающие устройства для разгрузчика будут следить за этим. Кроме того, вы можете использовать это, чтобы оставить закрытый код рядом с кодом инициализации, что может улучшить читаемость.
function setupWindow(window, document) {
var bound = this.contextPopupShowing.bind(this);
contextMenu.addEventListener('popupshowing', bound, false);
unloadWindow(window, function() {
contextMenu.removeEventListener('popupshowing', bound, false);
});
// Or stuff like
var element = document.createElement(...);
contextMenu.appendChild(element);
unloadWindow(window, function() {
contextMenu.removeChild(element);
});
// Or just combine the above into a single unloader
unloadWindow(window, function() {
contextMenu.removeEventListener('popupshowing', bound, false);
contextMenu.removeChild(element);
});
}
До bind()
был поддержан, вы должны были сохранить ссылку на this
вне функции. Затем передайте функцию, которая может переадресовать вызов так, как вы хотите.
var self = this;
contextMenu.addEventListener('popupshowing', function() {
self.contextPopupShowing.apply(self, arguments);
}, false);
В этом случае мы используем apply
установить контекст на self
Наша сохраненная версия this
и отправить его как угодно arguments
были переданы анонимной функции через магию arguments
Ключевое слово, которое содержит список аргументов, которые функция была передана при вызове.
Вам не нужно использовать bind
за addEventListener
, Ты можешь использовать handleEvent
, Именно в этой теме я тоже вас связал:
Удаление прослушивателя событий, который был добавлен с помощью bind
MDN:: EventTarget.addEventListener - значение "this" в обработчике
handleEvent
на самом деле это обычный способ, которым код javascript в кодовой базе Firefox делает это.
Скопировано прямо из MDN:
var Something = function(element) {
this.name = 'Something Good';
this.handleEvent = function(event) {
console.log(this.name); // 'Something Good', as this is the Something object
switch(event.type) {
case 'click':
// some code here...
break;
case 'dblclick':
// some code here...
break;
}
};
// Note that the listeners in this case are this, not this.handleEvent
element.addEventListener('click', this, false);
element.addEventListener('dblclick', this, false);
// You can properly remove the listners
element.removeEventListener('click', this, false);
element.removeEventListener('dblclick', this, false);
}
Где я в основном использую bind
это когда делает for
цикл и я делаю анонимные функции с чем-то в массиве, как arr[i]
, Если я не связываю его, то он всегда берет последний элемент массива, я понятия не имею, почему, и я ненавижу это, поэтому я перехожу к использованию [].forEach.call(arr, function(arrI)
,
http://2ality.com/2013/06/auto-binding.html
var listener = myWidget.handleClick.bind(myWidget);
domElement.addEventListener('click', listener);
...
domElement.removeEventListener(listener);