如何修复自定义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/spliceloop withfilter: This is cleaner and handles all matching callbacks in one pass. - Expanded the filter condition: Now it checks two cases:
- Is the item not the direct callback we want to remove (for
on-registered events)? - Is the item's
fnproperty (set in theoncewrapper) not equal to the original callback (foronce-registered events)?
- Is the item not the direct callback we want to remove (for
- Fixed the test code: Your original test had duplicate
function test(){};declarations, which throws an error in strict mode—I renamed the second one totestOnceto avoid this.
内容的提问来源于stack exchange,提问作者Sahin Erbay
相关产品推荐
相关产品推荐

