JavaScript可用闭包实现轻量安全的发布订阅模式,通过createEventEmitter返回私有handlers的on/emit/off接口,支持多实例隔离、once一次性监听及防内存泄漏设计。

JavaScript 中可以用闭包封装事件列表和操作方法,实现轻量、安全的发布订阅模式,避免全局变量污染,同时支持多个事件类型独立管理。
用闭包保存事件监听器集合
闭包的核心是让事件回调函数数组(handlers)被内部函数私有持有,外部无法直接访问或修改,只能通过约定的接口操作:
function createEventEmitter() {
const handlers = new Map(); // 每个事件名对应一个回调数组
return {
on(eventName, callback) {
if (!handlers.has(eventName)) {
handlers.set(eventName, []);
}
handlers.get(eventName).push(callback);
},
emit(eventName, ...args) {
const callbacks = handlers.get(eventName) || [];
callbacks.forEach(cb => cb(...args));
},
off(eventName, callback) {
if (!handlers.has(eventName)) return;
const list = handlers.get(eventName);
const index = list.indexOf(callback);
if (index > -1) {
list.splice(index, 1);
}
}
};
}
每次调用 createEventEmitter() 都生成一组独立的 handlers,互不干扰,适合模块化场景(如不同组件各自维护自己的事件系统)。
支持一次性监听(once)
在闭包内扩展功能很容易——比如添加 once 方法:监听一次后自动解绑。利用闭包捕获当前事件名和回调,再在执行后调用 off:
立即学习“Java免费学习笔记(深入)”;
once(eventName, callback) {
const onceWrapper = (...args) => {
callback(...args);
this.off(eventName, onceWrapper); // 注意:这里要解绑 wrapper 自身
};
this.on(eventName, onceWrapper);
}
关键点是把包装函数存下来,并传给 on;否则 off 找不到原始引用就无法清除。
防止内存泄漏的小技巧
- 监听器函数尽量避免使用匿名函数(尤其是箭头函数),否则无法在
off时准确移除 - 组件销毁前主动调用
off,或在on时返回一个取消函数(unsubscribe),更符合现代习惯 - 可选:为
on返回的取消函数绑定事件名和回调,方便统一清理
实际使用示例
创建实例后即可使用,完全隔离:
const bus1 = createEventEmitter();
const bus2 = createEventEmitter();
bus1.on('login', user => console.log('Bus1 收到登录:', user));
bus2.on('login', () => console.log('Bus2 忽略登录'));
bus1.emit('login', { id: 1 }); // 输出:Bus1 收到登录: {...}
bus2.emit('login'); // 输出:Bus2 忽略登录
每个 bus 都有自己独立的闭包作用域,事件不会交叉触发。


















