4

在插件禁用时引导插件中的 removeEventListener 不起作用之后,我正在探索其他可能性。

除了使用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

4 回答 4

3

由于我们正在谈论无需重启的附加组件...许多无需重启的附加组件使用unloadunloadWindow辅助功能,以使其更容易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);
  });
}
于 2014-06-29T07:07:36.813 回答
2

您不必使用bindfor addEventListener。您可以使用handleEvent. 我也在那个主题中链接了你:

删除使用绑定添加的事件侦听器

MDN :: EventTarget.addEventListener - 处理程序中“this”的值

handleEvent实际上是firefox代码库中的javascript代码执行此操作的常见方式。

直接从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).

于 2014-06-29T06:00:45.780 回答
2

bind()支持之前,您必须保存this对函数外部的引用。然后传入一个可以按照您想要的方式转发调用的函数。

var self = this;
contextMenu.addEventListener('popupshowing', function() {
     self.contextPopupShowing.apply(self, arguments);
}, false);

在这种情况下,我们使用apply将上下文设置为self,我们保存的版本,并将通过包含函数调用时传递的参数列表的魔法关键字传递给匿名函数的this任何内容发送给它。argumentsarguments

于 2014-06-29T05:14:51.757 回答
1

http://2ality.com/2013/06/auto-binding.html

var listener = myWidget.handleClick.bind(myWidget);
domElement.addEventListener('click', listener);
...
domElement.removeEventListener(listener);
于 2017-12-14T11:37:26.447 回答