Remove anonymous JavaScript function from array/object/Set

时间:2016-11-09 08:23:58

标签: javascript ecmascript-6 symbols

How can Node's emitter.removeListener be implemented in ES2015? Adding a callback to an array is easy:

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

How can that particular function be removed later, without registerCallback returning some identifier for the function? In other words, unregisterCallback(handler) should not need any other parameter, and should remove that handler. How would unregisterCallback check that an anonymous function had been previously added?

Is running handler.toString() (and potentially a hash function on that) a reliable solution to create an identifier for the function? Or how else should unregisterCallback iterate through callbacks to remove that particular element? (Or find the appropriate key in an object or function in a Set.)

mySet.add(function foo() { return 'a'})
mySet.has(function foo() { return 'a'})  // false

2 个答案:

答案 0 :(得分:3)

通常的解决方案是将函数本身作为参数传递给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");

但名称unicity的责任在于用户代码领域,这可能是好的,也可能不是,具体取决于您的应用程序。

答案 1 :(得分:1)

你可以去design,其中事件发射器返回一个可以更新内部状态的可调用对象:

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