观察者模式在JavaScript中由EventTarget原生支持,addEventListener/dispatchEvent即其标准实现;手写Observer需注意去重、异步执行和取消订阅;Vue/React的响应式是其思想的深度封装而非直接应用。

观察者模式在 JavaScript 中不是靠「实现」出来的,而是靠语言特性自然支撑的——它本质是事件监听的底层逻辑,addEventListener 就是它的标准封装。
为什么不用手写 Observer 类也能算用了观察者模式
因为浏览器原生的 EventTarget 接口(HTMLElement、document、window 等都继承它)已经完整实现了发布-订阅机制:
- 调用
addEventListener(type, callback)= 订阅 - 调用
dispatchEvent(event)= 发布 - 同一个事件类型可绑定多个回调,触发时全部执行 = 多观察者支持
- 回调执行顺序遵循注册顺序,且可被
once: true或removeEventListener控制生命周期
手写简易 Observer 时最容易漏掉的三个点
如果业务需要脱离 DOM 自建状态通知系统(比如响应式数据、跨模块通信),手写一个轻量 Observer 很常见,但以下细节常被忽略:
- 没做回调去重:同一函数多次
subscribe,notify时会重复执行 —— 应用Set存储回调或手动比对fn === existing - 没处理异步通知顺序:直接在
notify中同步遍历执行,若某个回调抛错,后续回调中断 —— 建议统一用Promise.resolve().then(() => fn())包裹 - 没提供取消订阅接口:只留
subscribe没留unsubscribe,导致内存泄漏风险 —— 必须返回取消函数,或接受callback作为卸载依据
Vue / React 中的「响应式」和观察者模式是什么关系
它们借用了观察者思想,但实现上已远超经典模式:
立即学习“Java免费学习笔记(深入)”;
- Vue 2 的
Object.defineProperty+ 依赖收集,每个响应式属性内部维护一个Dep实例(即主题),Watcher实例(即观察者)在取值时自动订阅 —— 这是编译期+运行期协同的隐式订阅 - React 的
useState和useEffect不暴露订阅 API,更新由dispatchAction触发,内部用链表管理更新队列 —— 它更接近「状态驱动重渲染」,观察者逻辑被框架深度封装 - 二者都不鼓励你手动维护
subscribe/unsubscribe,而是通过 Hook/选项声明式表达依赖 —— 所以别在 React 组件里自己 new Observer,优先用useReducer或Context+useContext
class SimpleObserver {
constructor() {
this.callbacks = new Set();
}
subscribe(fn) {
this.callbacks.add(fn);
return () => this.callbacks.delete(fn);
}
notify(data) {
this.callbacks.forEach(fn => {
Promise.resolve().then(() => fn(data));
});
}
}
// 使用示例
const obs = new SimpleObserver();
const unsubscribe = obs.subscribe(console.log);
obs.notify('hello'); // → 'hello'
unsubscribe(); // 取消监听
obs.notify('world'); // 无输出
真正难的不是写出一个能跑的 Observer,而是判断什么时候不该用它——比如单个组件内的状态变化,用 useState 更直接;两个紧耦合模块通信,用 props / emit 更清晰;只有跨层级、低耦合、多消费者的通知场景,才值得引入显式的观察者抽象。










