闭包在发布订阅模式中通过函数作用域封装私有监听列表,提供on/emit/off等受控接口;工厂函数方式确保方法共享同一闭包变量,避免this绑定问题,支持once和防内存泄漏设计。

闭包在发布订阅模式中维护私有事件监听列表,核心是利用函数作用域“捕获”外部变量,使监听器数组不被外部直接访问,同时提供受控的增删、触发接口。
用闭包封装 listeners 数组
把事件监听器列表(如 listeners = {})声明在构造函数或工厂函数内部,不暴露给外部。所有操作监听器的方法(on、emit、off)都定义在同一个闭包作用域中,自然共享该数组。
例如:
function createEventBus() {
const listeners = {}; // ✅ 私有,外部无法访问
return {
on(event, callback) {
if (!listeners[event]) listeners[event] = [];
listeners[event].push(callback);
},
emit(event, data) {
(listeners[event] || []).forEach(cb => cb(data));
},
off(event, callback) {
if (listeners[event]) {
listeners[event] = listeners[event].filter(cb => cb !== callback);
}
}
};
}
const bus = createEventBus();
bus.on('click', () => console.log('handled'));
bus.emit('click'); // ✅ 可触发
console.log(bus.listeners); // ❌ undefined —— 真正私有
避免 this 绑定导致闭包失效
如果把方法写成类的方式但未正确绑定,回调中可能丢失对闭包内 listeners 的引用。推荐用箭头函数、显式绑定或工厂函数(如上例),而非依赖 this 指向实例属性。
- ❌ 错误:类字段 + 普通函数方法 →
this.listeners依赖实例属性,不私有 - ✅ 正确:工厂函数返回对象,所有方法共享同一份闭包变量
- ✅ 替代:ES6 class 中用私有字段
#listeners(非闭包但同样私有)
支持一次性监听与事件清理
借助闭包,还能轻松实现 once 方法:内部自动注册监听器,并在首次触发后调用 off 清理自身。
示例增强:
function createEventBus() {
const listeners = {};
function off(event, callback) {
if (listeners[event]) {
listeners[event] = listeners[event].filter(cb => cb !== callback);
}
}
return {
on(event, callback) {
if (!listeners[event]) listeners[event] = [];
listeners[event].push(callback);
},
once(event, callback) {
const onceCallback = (...args) => {
callback(...args);
off(event, onceCallback); // ✅ 闭包内可直接调用 off
};
this.on(event, onceCallback);
},
emit(event, ...args) {
(listeners[event] || []).forEach(cb => cb(...args));
},
off
};
}
注意内存泄漏风险
闭包让监听器长期持有对回调函数的引用,若忘记 off,且回调又引用了大对象(如 DOM 节点、大型数据),就可能造成内存泄漏。
- 确保组件卸载、模块销毁时主动解绑
- 可扩展
off支持按事件清空,或返回取消函数(unsubscribe) - 调试时检查
listeners是否持续增长
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











