闭包通过封装事件映射表和操作接口实现事件总线:内部维护私有events对象,暴露on/emit/off方法,确保状态隔离、防篡改且无全局污染。

闭包本身不直接实现跨组件通信,但它能为事件总线(Event Bus)这类模式提供轻量、安全的状态封装基础。关键在于:用闭包捕获并持久化事件监听器集合与分发逻辑,避免全局变量污染,同时确保内部状态不被外部篡改。
闭包如何支撑事件总线的核心结构
一个典型的事件总线需要维护两样东西:事件名到回调函数列表的映射、以及发布/订阅/取消的接口。这些数据本该放在全局或模块顶层,但用闭包可将其“私有化”——只暴露方法,隐藏状态。
- 外部作用域定义 events 对象(如
{ click: [cb1, cb2], submit: [cb3] }),它在闭包内被所有方法共享 - 返回的 subscribe/publish/unsubscribe 函数都持有对这个
events的引用,即使创建它们的外层函数已执行完毕,该对象仍存活 - 外部代码无法直接读写
events,只能通过返回的方法操作,实现了数据封装和访问控制
手写一个基于闭包的轻量事件总线
以下是一个无依赖、纯闭包实现的 EventBus 示例:
(适用于 React/Vue/原生 JS 等任意环境)- 调用
createEventBus()得到一个独立实例,多个实例互不影响 -
subscribe(event, cb)把回调存入对应事件队列 -
publish(event, data)遍历并执行该事件的所有回调,传入data - 所有状态(
events)仅在闭包内可见,不会泄漏到全局作用域
在 React 中结合闭包总线做跨组件通信
不依赖 Context 或 Redux,适合中等复杂度场景:
- 在模块顶层调用
const bus = createEventBus(),得到单例总线(也可按需多例) - 组件 A 触发动作时调用
bus.publish('userLogin', { id: 123 }) - 组件 B 在
useEffect中bus.subscribe('userLogin', handleLogin),并在卸载时unsubscribe - 由于闭包维持了
events引用,B 即使在 A 之后挂载,也能收到后续发布的事件
为什么比直接用全局对象更可靠
闭包带来的实际好处不是“功能增强”,而是工程健壮性提升:
-
避免命名冲突:每个
createEventBus()返回独立作用域,不同模块可各自管理事件空间 -
防止误修改:没有全局
window.events,别人不能随意清空或覆盖监听器列表 - 利于测试与销毁:可为单元测试创建临时总线实例,用完即弃,不影响其他测试
- 天然支持模块隔离:配合 ES Module,总线状态完全封闭在模块内,符合现代前端封装原则

















