事件总线通过闭包封装内部状态,暴露on、emit、off等最小接口,支持多实例隔离、once一次性监听和onAny通配符监听,确保私有性与实用性。

用闭包和块级作用域设计事件总线,核心是**隔离内部状态、暴露最小必要接口、避免全局污染**。不需要类或 this,靠函数作用域封装订阅/发布逻辑,用 const 声明的 Map 或对象保存事件监听器即可。
用闭包封装事件存储与核心方法
创建一个立即执行函数,返回只含 on(订阅)、emit(触发)、off(取消)三个方法的对象。所有监听器存在闭包内的 Map 中,外部无法直接访问:
const EventBus = (() => {
const listeners = new Map(); // 键为事件名,值为回调数组
const on = (event, callback) => {
if (!listeners.has(event)) listeners.set(event, []);
listeners.get(event).push(callback);
};
const emit = (event, data) => {
const callbacks = listeners.get(event) || [];
callbacks.forEach(cb => cb(data));
};
const off = (event, callback) => {
if (!listeners.has(event)) return;
const list = listeners.get(event);
const index = list.indexOf(callback);
if (index > -1) list.splice(index, 1);
};
return { on, emit, off };
})();
用块级作用域支持多实例且互不干扰
如果需要多个独立事件总线(比如模块 A 和模块 B 各自维护自己的事件流),把上面逻辑封装成工厂函数,每次调用都生成新闭包:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
const createEventBus = () => {
const listeners = new Map();
return {
on: (event, callback) => {
if (!listeners.has(event)) listeners.set(event, []);
listeners.get(event).push(callback);
},
emit: (event, data) => {
(listeners.get(event) || []).forEach(cb => cb(data));
},
off: (event, callback) => {
const list = listeners.get(event);
if (list) {
const i = list.indexOf(callback);
if (i > -1) list.splice(i, 1);
}
}
};
};
// 两个完全隔离的总线
const busA = createEventBus();
const busB = createEventBus();
busA.on('msg', console.log); // 只响应 busA.emit
busB.on('msg', () => alert('B got it')); // busA.emit 不触发它
增强实用性:支持一次性监听与通配符
在基础版本上加两个常见功能:once(监听一次后自动移除)、onAny(监听所有事件)。仍保持闭包内状态私有:
立即学习“Java免费学习笔记(深入)”;
- once:包装回调,内部调用 off 自清理
- onAny:单独存一个数组,emit 时遍历它;注意它不参与事件名匹配,而是额外广播
const createEventBus = () => {
const listeners = new Map();
const anyListeners = [];
const on = (event, callback) => {
if (!listeners.has(event)) listeners.set(event, []);
listeners.get(event).push(callback);
};
const once = (event, callback) => {
const wrapper = (...args) => {
callback(...args);
off(event, wrapper);
};
on(event, wrapper);
};
const onAny = (callback) => anyListeners.push(callback);
const emit = (event, data) => {
(listeners.get(event) || []).forEach(cb => cb(data));
anyListeners.forEach(cb => cb(event, data));
};
const off = (event, callback) => {
const list = listeners.get(event);
if (list) {
const i = list.indexOf(callback);
if (i > -1) list.splice(i, 1);
}
};
return { on, once, onAny, emit, off };
};
注意事项与边界处理
- 回调函数引用必须一致才能正确 off —— 箭头函数或匿名函数每次都是新引用,建议将回调赋给变量再传入
- emit 时若回调抛错,建议 try/catch 包裹单个调用,避免一个错误中断后续监听器
- 大型应用中可加事件命名空间支持(如 'user:login'),用 split(':') 分层管理,但非必需
- 不需要 WeakMap,因为监听器通常随模块生命周期存在,手动 off 更可控

















