JavaScript 如何实现观察者模式
在日常开发中,你有没有过这种经历:页面上的购物车商品数量变了,导航栏右上角的数字自动跟着更新;或者你订阅了某个公众号,一旦作者发新文章,你立刻能收到推送?其实这些场景背后,藏着一种很实用的设计模式——观察者模式。而 JavaScript 因为灵活的事件机制,实现这个模式特别简单。
什么是观察者模式?用生活例子秒懂
先别被“模式”两个字吓住,它本质就是“一对多的通知关系”。举个生活化的例子:小区里的快递驿站,会有很多住户来取快递。如果驿站给每个住户单独发消息说“你的快递到了”,不仅麻烦,还可能漏发;但换个方式,住户只要提前在驿站登记“我要接收快递通知”(订阅),驿站只要有新快递到,就给所有登记过的住户发消息(广播通知),没人会错过,也不用反复操作。

放到代码里,这个逻辑对应两个角色:
- 观察者:就是那些“想要接收通知”的对象,比如住户;
- 目标(被观察者):就是“提供通知的主体”,比如快递驿站。
目标会维护一个观察者列表,新增观察者(订阅)、移除观察者(退订),还能在自己状态变化时,遍历列表通知所有观察者。
JavaScript 实现观察者模式的核心步骤
在 JS 里,不用复杂的工具,用几个简单的方法就能搭出这个模式,我们用刚才的快递例子对应代码:
第一步:写一个“目标”类,管理观察者列表
目标类的核心是三个方法:添加观察者、移除观察者、通知所有观察者。我们给它起个名字叫 DeliveryStation(快递驿站):
`javascript
class DeliveryStation {
constructor() {
// 用数组存所有登记的住户(观察者)
this.observers = [];
}
// 订阅:把新住户加到列表里
subscribe(observer) {
this.observers.push(observer);
console.log(${observer.name} 成功订阅快递通知);
}
// 退订:把住户从列表里去掉
unsubscribe(observer) {
this.observers = this.observers.filter(item => item !== observer);
console.log(${observer.name} 已退订快递通知);
}
// 状态变化:有新快递到,通知所有观察者
notify(快递信息) {
this.observers.forEach(observer => {
observer.update(快递信息);
});
}
}
`
第二步:写一个“观察者”类,定义收到通知的反应
观察者的核心是一个 update 方法,用来处理收到通知后的动作。这里的观察者就是住户,比如小张、小李:
`javascript
class Resident {
constructor(name) {
this.name = name;
}
// 收到快递通知后的反应
update(快递信息) {
console.log(${this.name} 收到通知:${快递信息});
}
}
`
第三步:用起来,看看实际效果
现在把代码串起来,模拟一次快递到的场景:
`javascript
// 初始化驿站和住户
const station = new DeliveryStation();
const zhang = new Resident('小张');
const li = new Resident('小李');
// 小张和小李订阅通知
station.subscribe(zhang); // 输出:小张成功订阅快递通知
station.subscribe(li); // 输出:小李成功订阅快递通知
// 驿站有新快递到,发通知
station.notify('您的快递(取件码1234)已送达驿站');
// 输出:小张收到通知:您的快递(取件码1234)已送达驿站
// 输出:小李收到通知:您的快递(取件码1234)已送达驿站
// 小张退订,再发通知试试
station.unsubscribe(zhang); // 输出:小张已退订快递通知
station.notify('您的另一个快递(取件码5678)已送达驿站');
// 输出:小李收到通知:您的另一个快递(取件码5678)已送达驿站
`
你看,小张退订后就再也收不到通知了,完全符合我们的需求。
实际开发里的常见用法
可能有人会问,这种模式在真实项目里用在哪?比如你做一个待办事项列表,新增一个待办时,不仅要显示在列表里,还要统计“待办数量”更新到页面角落;或者做一个主题切换功能,用户选了深色模式,页面的所有按钮、背景都要自动变成深色——这些场景里,“待办列表”“主题切换器”就是被观察者,“列表渲染器”“页面元素”就是观察者,用观察者模式能把代码拆得更清晰,不用写一堆重复的逻辑。
而且 JS 里其实有现成的简化方案,比如用 EventTarget 类,或者第三方库像 RxJS,能处理更复杂的异步通知,但核心逻辑还是我们刚才说的“订阅-通知”的一对多关系。