Передача 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);
Другие вопросы по тегам