观察者模式在JavaScript中由EventTarget原生支持,addEventListener/dispatchEvent即其标准实现;手写Observer需注意去重、异步执行和取消订阅;Vue/React的响应式是其思想的深度封装而非直接应用。
观察者模式在 JavaScript 中不是靠「实现」出来的,而是靠语言特性自然支撑的——它本质是事件监听的底层逻辑,addEventListener 就是它的标准封装。
因为浏览器原生的 EventTarget 接口(HTMLElement、document、window 等都继承它)已经完整实现了发布-订阅机制:
addEventListener(type, callback) = 订阅dispatchEvent(event) = 发布once: true 或 removeEventListener 控制生命周期如果业务需要脱离 DOM 自建状态通
知系统(比如响应式数据、跨模块通信),手写一个轻量 Observer 很常见,但以下细节常被忽略:
subscribe,notify 时会重复执行 —— 应用 Set 存储回调或手动比对 fn === existing
notify 中同步遍历执行,若某个回调抛错,后续回调中断 —— 建议统一用 Promise.resolve().then(() => fn()) 包裹subscribe 没留 unsubscribe,导致内存泄漏风险 —— 必须返回取消函数,或接受 callback 作为卸载依据它们借用了观察者思想,但实现上已远超经典模式:
Object.defineProperty + 依赖收集,每个响应式属性内部维护一个 Dep 实例(即主题),Watcher 实例(即观察者)在取值时自动订阅 —— 这是编译期+运行期协同的隐式订阅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 更清晰;只有跨层级、低耦合、多消费者的通知场景,才值得引入显式的观察者抽象。