通过这个'和addEventListener函数的参数,而不使用bind

时间:2014-06-29 05:03:20

标签: javascript firefox firefox-addon firefox-addon-restartless

removeEventListener in bootstrapped addon not working when addon disabled之后,我正在探索其他可能性。

除了使用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 个答案:

答案 0 :(得分: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和/或代码模块的Zombie compartments(这来自经验编写和查看无重新启动的附加组件)。

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);
  });
}

答案 1 :(得分:2)

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

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

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

答案 2 :(得分:2)

您不必将bind用于addEventListener。您可以使用handleEvent。在那个主题中,我也把你联系起来了:

Removing event listener which was added with bind

MDN :: EventTarget.addEventListener - The value of "this" within the handler

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)

答案 3 :(得分:0)

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

var listener = myWidget.handleClick.bind(myWidget);
domElement.addEventListener('click', listener);
...
domElement.removeEventListener(listener);