从数组/对象/Set中删除匿名JavaScript函数

6

如何在ES2015中实现Node的emitter.removeListener?将回调添加到数组很容易:

let callbacks = [];
function registerCallback(handler) {
    callbacks.push(handler);
});

如何在不返回某个函数标识符的情况下,稍后删除特定功能?换句话说,unregisterCallback(handler)不应需要任何其他参数,并且应删除该处理程序。 unregisterCallback 如何检查是否先前添加了匿名函数?
运行handler.toString()(并潜在地对其进行哈希函数)是创建函数标识符的可靠解决方案吗?或者unregisterCallback应如何迭代callbacks以删除特定元素?(或在对象中找到适当的键或在集合中找到函数。)
mySet.add(function foo() { return 'a'})
mySet.has(function foo() { return 'a'})  // false
2个回答

4
通常的解决方案是将函数本身作为参数传递给unregisterCallback函数。例如在jQuery中就是这样做的。
因此,unregisterCallback函数只需要使用indexOf来查找回调的索引:
function unregisterCallback(handler) {
    var index = callbacks.indexOf(handler);
    if (~index) callbacks.splice(index, 1);
}
registerCallback调用中定义的函数无法正常工作,用户代码必须保留该函数。
以下写法不可行:
registerCallback(function foo() { return 'a'});
// later...
unregisterCallback(function foo() { return 'a'}); // this is a different function

这个是有效的:

function foo(){
    return 'a'
}
registerCallback(foo);
// later...
unregisterCallback(foo); // it's the same function

您还可以提供按名称删除的功能:
// pass either the function or its name
function unregisterCallback(handler) {
  var index = -1;
  if (typeof handler==="string") {
      for (var i=0; i<callbacks.length; i++) {
        if (callbacks[i].name===handler) {
          index = i;
          break;
        }
      }
  } else {
      index = callbacks.indexOf(handler);
  }
  if (~index) callbacks.splice(index, 1);
}
registerCallback(function foo() { return 'a'});
unregisterCallback("foo");

但是名称唯一性的责任落在用户代码领域中,这可能是可以接受的,也可能不是,这取决于您的应用程序。


1
谢谢。将函数本身用作键也适用于集合。 - Dan Dascalescu
1
有趣的是,setTimeout()setInterval()返回一个标识符,而不是clearTimeout()clearInterval()使用相同的函数。 - Dan Dascalescu
@DanDascalescu 是的,这很有趣。这可能是因为这些函数是在 JavaScript 的“函数作为一等公民”(https://en.wikipedia.org/wiki/First-class_function)的本质在文化上显而易见之前设计的。JavaScript 最初设计得非常快,许多最初的设计可以追溯到在这个领域表现不佳的 Java。这也可能是实现泄漏的情况。 - Denys Séguret
@DanDascalescu 有人指出,在setTimeoutsetInterval中使用函数本身会使得在多个计时器中使用同一函数变得更加困难。 - Denys Séguret
你好!我知道这是老问题了,抱歉,但我必须问一下。这个 if (~index) 到底是怎么工作的?我从未见过这种语法。 - matronator

1

您可以选择 设计,其中事件发射器返回一个可调用对象,将更新内部状态:

const dispose = sleep.on('sheep', ::sleep.tick)
sleep.once('baanough', dispose)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接