ES6中Emitter类off方法与同名导出函数的复用问题咨询
嘿,这个问题我之前帮朋友排查过类似的场景,咱们一步步拆解清楚~
解决方案详解
首先得明确一个核心点:你写的Emitter类里的off是实例方法——也就是说必须先创建Emitter的实例,才能调用这个方法。而你导出的独立off是模块级的函数,要在里面调用类的实例方法,关键是要有一个合法的Emitter实例可以使用。
1. 最简洁的通用方案:模块单例模式
如果你的业务场景只需要一个全局的事件发射器实例,那这是最省心的实现方式:在模块内部创建一个Emitter单例,然后让独立的off函数直接调用这个单例的off方法。代码如下:
export class Emitter { constructor() { this.events = {}; } off(eventName, callBack) { console.log(this); // 现在这里会指向Emitter实例,不再是空对象 // 补充实际的事件移除逻辑(原代码里只是打日志,这里补全一下) if (this.events[eventName]) { this.events[eventName] = this.events[eventName].filter(fn => fn !== callBack); } } } // 模块内部创建一个全局单例,外部不会直接访问到 const defaultEmitter = new Emitter(); // 模块级的off函数,直接代理到单例的off方法 export function off(eventName, callBack) { defaultEmitter.off(eventName, callBack); }
这样其他文件导入后调用Emitter.off(EVENT_NAME_ONE, callback)时,本质就是操作这个全局单例的事件系统,代码简洁且完全符合你的导入调用方式。
2. 更灵活的进阶方案:支持自定义实例
如果需要让模块级的off函数能操作任意Emitter实例(比如多实例场景),可以把实例作为可选参数传入,兼顾便捷性和灵活性:
export class Emitter { constructor() { this.events = {}; } off(eventName, callBack) { if (this.events[eventName]) { this.events[eventName] = this.events[eventName].filter(fn => fn !== callBack); } } } const defaultEmitter = new Emitter(); // 支持传入自定义实例,默认使用全局单例 export function off(eventName, callBack, emitter = defaultEmitter) { // 加个类型校验,避免传入非法参数 if (!(emitter instanceof Emitter)) { throw new TypeError('The third argument must be an instance of Emitter'); } emitter.off(eventName, callBack); }
这种方式下,你既可以直接用默认实例:
import * as Emitter from '../src/emitter'; Emitter.off(EVENT_NAME_ONE, myCallback);
也可以指定自己创建的实例:
import * as Emitter from '../src/emitter'; const myCustomEmitter = new Emitter.Emitter(); Emitter.off(EVENT_NAME_ONE, myCallback, myCustomEmitter);
关于静态方法的误区
这里提个小提醒:如果你的off方法不需要访问实例的this.events(比如用全局事件池),可以把Emitter的off改成静态方法,但从你原代码里的this.events来看,这明显是实例级的事件存储,所以静态方法并不适合这个场景,就不推荐了。
内容的提问来源于stack exchange,提问作者Sahin Erbay
相关产品推荐
相关产品推荐

