观察者模式强调目标与观察者直接引用,发布-订阅通过事件中心解耦;前者适用于固定依赖同步通知,后者适合跨模块动态通信,二者均需注意内存泄漏防范。

JavaScript 中的观察者模式(Observer Pattern)和发布-订阅模式(Pub/Sub)常被混用,但二者有关键区别:观察者模式强调目标对象与观察者之间的**直接引用关系**,而发布-订阅通过**事件中心(中介)解耦发布者与订阅者**,更适合大型应用中跨模块通信。
观察者模式:目标主动通知已知观察者
适用于一个状态变化需同步通知多个固定依赖对象的场景,比如 UI 组件响应数据模型更新。
- 目标(Subject)维护观察者列表,提供
attach、detach、notify方法 - 观察者(Observer)实现统一更新接口(如
update()) - 目标在状态变更后遍历调用每个观察者的
update()
示例:
class Subject {
constructor() {
this.observers = [];
}
attach(observer) {
this.observers.push(observer);
}
detach(observer) {
this.observers = this.observers.filter(obs => obs !== observer);
}
notify(data) {
this.observers.forEach(obs => obs.update(data));
}
}
class Observer {
constructor(name) {
this.name = name;
}
update(data) {
console.log(`${this.name} received:`, data);
}
}
const subject = new Subject();
const o1 = new Observer('PanelA');
const o2 = new Observer('PanelB');
subject.attach(o1);
subject.attach(o2);
subject.notify('data changed'); // 两者均收到
发布-订阅模式:靠事件总线实现完全解耦
发布者不关心谁订阅,订阅者也不关心谁发布,所有通信经由一个中央事件通道(EventBus)中转,适合松散耦合、动态增删监听器的场景。
立即学习“Java免费学习笔记(深入)”;
- 核心是
on(订阅)、emit(发布)、off(取消)三个方法 - 内部用对象按事件名分类存储回调函数数组
- 支持通配符(如
user.*)或一次性事件(once)可扩展
简易实现:
class EventBus {
constructor() {
this.events = {};
}
on(event, callback) {
if (!this.events[event]) this.events[event] = [];
this.events[event].push(callback);
}
emit(event, ...args) {
if (this.events[event]) {
this.events[event].forEach(cb => cb(...args));
}
}
off(event, callback) {
if (this.events[event]) {
this.events[event] = this.events[event].filter(cb => cb !== callback);
}
}
}
const bus = new EventBus();
bus.on('user.login', (user) => console.log('Welcome,', user.name));
bus.on('user.logout', () => console.log('Bye!'));
bus.emit('user.login', { name: 'Alice' }); // 触发对应监听器
Vue 和 React 中的实践参考
现代框架已封装类似机制,无需手动实现,但理解原理有助于调试和设计自定义逻辑:
- Vue 2 的
$on/$emit是典型的发布-订阅(仅限组件实例内);Vue 3 的provide/inject+ 自定义事件更灵活 - React 中可用 Context + useReducer 模拟全局事件总线,或借助
useEffect监听状态变化实现观察者语义 - 状态管理库如 Redux 的 store.subscribe 本质是观察者模式;其 action dispatch 则接近发布-订阅
何时选哪种?关键看耦合粒度
如果模块之间存在明确的“谁依赖谁”且生命周期可控(如表单校验器监听输入框),观察者模式更轻量直观;若模块分散、通信关系动态多变(如登录态变更通知菜单、头像、权限按钮),发布-订阅更健壮易维护。
不复杂但容易忽略:避免内存泄漏——观察者需手动 detach,订阅者需及时 off;推荐在组件卸载或实例销毁时清理监听。










