继removeEventListener在bootstrap插件禁用时不工作后,我正在探索其他可能性。
除了使用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()
的替代方法
我甚至试图用<menupopup id="contentAreaContextMenu" ...>
的递归函数抓取window
更新: bind()
干扰removeEventListener
既然我们说的是不启动的附加组件…许多不重启的附加组件使用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
传递给匿名函数,该关键字包含函数调用时传递的参数列表。
您不必为addEventListener
使用bind
。您可以使用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)
。
http://2ality.com/2013/06/auto-binding.html
var listener = myWidget.handleClick.bind(myWidget);
domElement.addEventListener('click', listener);
...
domElement.removeEventListener(listener);