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

Dan*_*scu 5 javascript symbols ecmascript-6

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

let callbacks = [];
function registerCallback(handler) {
    callbacks.push(handler);
});
Run Code Online (Sandbox Code Playgroud)

以后如何删除特定功能,而无需registerCallback返回该功能的某些标识符?换句话说,unregisterCallback(handler)应该不需要任何其他参数,并且应该删除该处理程序。如何unregisterCallback检查以前是否已添加匿名函数?

运行handler.toString()(并可能在其上使用哈希函数)为该函数创建标识符的可靠解决方案吗?还是应该unregisterCallback迭代其他步骤callbacks以删除该特定元素?(或者在集合的对象或函数中找到适当的键。)

mySet.add(function foo() { return 'a'})
mySet.has(function foo() { return 'a'})  // false
Run Code Online (Sandbox Code Playgroud)

Den*_*ret 4

通常的解决方案是将函数本身作为参数传递给函数unregisterCallback。例如,这就是 jQuery 中所做的事情。

因此该unregisterCallback函数只需使用indexOf来查找回调的索引:

function unregisterCallback(handler) {
    var index = callbacks.indexOf(handler);
    if (~index) callbacks.splice(index, 1);
}
Run Code Online (Sandbox Code Playgroud)

当然,这意味着用户代码必须保留该函数,它不能是调用中定义的函数registerCallback

这不起作用:

registerCallback(function foo() { return 'a'});
// later...
unregisterCallback(function foo() { return 'a'}); // this is a different function
Run Code Online (Sandbox Code Playgroud)

这有效:

function foo(){
    return 'a'
}
registerCallback(foo);
// later...
unregisterCallback(foo); // it's the same function
Run Code Online (Sandbox Code Playgroud)

您还可以提供按名称删除的功能:

// 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");
Run Code Online (Sandbox Code Playgroud)

但是名称唯一性的责任属于用户代码领域,这可能可以,也可以不行,具体取决于您的应用程序。