观察者模式与发布-订阅模式本质区别在于耦合度与通信方式:前者是主体与观察者直接同步调用,后者通过事件总线异步解耦通信。

JavaScript 中观察者模式和发布-订阅模式常被混淆,但二者在结构、依赖关系和通信方式上有本质区别。观察者模式强调“一对多”的同步依赖,目标对象(Subject)直接持有观察者(Observer)的引用;而发布-订阅模式通过“事件总线”解耦双方,依赖中介(Event Bus)完成异步消息分发。
观察者模式:主体与观察者直接关联
观察者模式中,被观察的目标(Subject)维护一个观察者列表,当状态变化时,主动遍历并调用每个观察者的更新方法。观察者需显式订阅(attach)和退订(detach),且必须了解 Subject 的存在。
典型实现要点:
- Subject 提供 addObserver、removeObserver 和 notify 方法
- Observer 实现统一的 update 接口(或回调函数)
- Subject 与 Observer 是强引用关系,生命周期易耦合
- 通知是同步的,执行顺序即注册顺序
例如:DOM 事件监听本质上是观察者模式的变体——元素(Subject)持有事件处理函数(Observer)的引用,触发 click 时同步调用 handler。
立即学习“Java免费学习笔记(深入)”;
发布-订阅模式:靠事件中心解耦通信
发布-订阅模式引入第三方“事件总线”(Event Bus 或 PubSub),发布者(Publisher)只向总线发消息,订阅者(Subscriber)只向总线注册回调。双方互不知晓,也不持有对方引用。
关键特征:
- 总线提供 on(订阅)、off(取消)、emit(发布)方法
- 支持按事件类型(如 "user:login")过滤,可一对多、多对多、甚至零对多
- 消息可缓存、延迟派发,支持异步、队列、中间件等扩展
- 天然支持跨组件、跨模块通信,常见于 Vue 的 $bus、Node.js 的 EventEmitter
核心差异对比
二者最根本的不同在于耦合程度与通信路径:
- 依赖关系:观察者模式中 Observer 知道 Subject,Subject 也持有 Observer;发布-订阅中 Publisher 和 Subscriber 都只依赖 Event Bus
- 通信方式:观察者是同步、即时、点对点的;发布-订阅是异步、事件驱动、基于主题(topic)的间接通信
- 灵活性:发布-订阅支持动态事件名、通配符匹配(如 "user:*")、优先级、once 订阅等,观察者模式通常固定在单一状态变更上
- 内存管理:观察者若忘记 detach 易导致内存泄漏;发布-订阅虽也需 off,但总线可提供自动清理策略(如组件销毁时批量清除)
简单代码示意
观察者模式简写:
class Subject {
constructor() { this.observers = []; }
addObserver(o) { this.observers.push(o); }
notify(data) { this.observers.forEach(o => o.update(data)); }
}
class Observer { update(data) { console.log('Received:', data); } }
发布-订阅简写:
class EventBus {
constructor() { this.events = {}; }
on(type, fn) { (this.events[type] = this.events[type] || []).push(fn); }
emit(type, data) { this.events[type]?.forEach(fn => fn(data)); }
}
注意:实际项目中推荐使用成熟的库(如 mitt、tiny-emitter)或框架内置机制(React 的 Context + useReducer、Vue 的 provide/inject + emits),避免手写总线带来的边界问题。










