You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何修复自定义Emitter中once注册事件无法被off移除的问题?

Fixing the off Method to Remove once-Registered Events

Hey there, let's break down why your off method isn't handling events registered with once—and how to fix it. The core issue is that when you use once, you're storing a wrapper function (g) in your event list instead of the original callback (fn). So when you call off('test', test), your code looks for the original test function in the list, but can't find it because the list only has the wrapped g function.

Here's the revised code that fixes this problem, plus explanations of the key changes:

class Emitter {
  constructor() {
    this.events = {};
  }

  on(eventName, callBack) {
    if (!this.events[eventName]) {
      this.events[eventName] = [];
    }
    this.events[eventName].push(callBack);
    return this.events[eventName].length;
  }

  off(eventName, callBack) {
    let list = this.events[eventName];
    if (list) {
      if (!callBack) {
        delete this.events[eventName];
      } else {
        // Filter out both direct callbacks and wrapped once callbacks
        this.events[eventName] = list.filter(item => {
          // Keep items that aren't the target callback, and aren't the wrapped version of it
          return item !== callBack && item.fn !== callBack;
        });
        // Clean up the event key if no callbacks are left
        if (this.events[eventName].length === 0) {
          delete this.events[eventName];
        }
      }
    }
    return (this.events[eventName] || []).length;
  }

  once(event, fn) {
    if (typeof event !== 'string') throw Error();
    if (typeof fn !== 'function') throw Error();

    function g() {
      this.off(event, g);
      fn.apply(this, arguments);
    }
    g.fn = fn;
    this.on(event, g);
    return this.events[event].length;
  };
}

// Updated test code (fixed duplicate function declaration)
let e = new Emitter();
console.log(e.events); // {}

function testRegular(){};
e.on('test', testRegular);
console.log(e.events); // { test: [ [Function: testRegular] ] }
e.off('test', testRegular);
console.log(e.events); // {}

console.log('==========');

console.log(e.events); // {}
function testOnce(){};
e.once('test', testOnce);
console.log(e.events); // { test: [ [Function: g] ] }
e.off('test', testOnce);
console.log(e.events); // {}

Key Changes Explained:

  • Replaced the indexOf/splice loop with filter: This is cleaner and handles all matching callbacks in one pass.
  • Expanded the filter condition: Now it checks two cases:
    1. Is the item not the direct callback we want to remove (for on-registered events)?
    2. Is the item's fn property (set in the once wrapper) not equal to the original callback (for once-registered events)?
  • Fixed the test code: Your original test had duplicate function test(){}; declarations, which throws an error in strict mode—I renamed the second one to testOnce to avoid this.

内容的提问来源于stack exchange,提问作者Sahin Erbay

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 07:10:07