46. 实现一个发布订阅系统(观察者模式) 副本.md 来源:https://my.feishu.cn/file/Y9uobTr2boysiSxbE3Yc0yrKnPh 采集状态:已完成正文采集;原文件源码视图逐行采集,行号 1 至 125 连续,滚动到底。 ### 1. 核心概念 (Core Concept) 发布订阅系统,或称观察者模式 (Observer Pattern),是一种软件设计模式,其中一个对象(称为**主题 Subject** 或**发布者 Publisher**)维护一个依赖它的对象列表(称为**观察者 Observers** 或**订阅者 Subscribers**),当主题状态发生改变时,会自动通知所有观察者。 ### 2. 为什么需要它? (The "Why") * **解耦 (Decoupling):** 发布者和订阅者之间通过一个中介(事件中心或主题)进行通信,彼此无需知道对方的具体实现,降低了模块间的耦合度。 * **灵活性 (Flexibility):** 可以轻松地添加或移除观察者,而不会影响发布者或其他观察者。 * **通知机制 (Notification):** 提供了一种有效的通信机制,当某个事件发生时,可以广而告之,让所有感兴趣的对象都能及时响应。 ### 3. API 与用法 (API & Usage) 实现一个简单的发布订阅系统,通常需要实现以下核心方法: * **`subscribe(eventType, handler)`:** 订阅某个类型的事件,当该事件发生时,执行相应的处理函数 `handler`。 * **`unsubscribe(eventType, handler)`:** 取消订阅某个类型的事件及对应的处理函数。 * **`publish(eventType, ...args)`:** 发布某个类型的事件,并携带可选参数 `...args`,通知并执行所有订阅了该事件的处理函数。 以下是一个简单的 JavaScript 实现示例: ```javascript class EventEmitter { constructor() { // 用于存储事件类型及其对应的处理函数列表 this.events = {}; } /** * 订阅事件 * @param {string} eventType 事件类型 * @param {function} handler 事件处理函数 */ subscribe(eventType, handler) { if (!this.events[eventType]) { this.events[eventType] = []; } this.events[eventType].push(handler); } /** * 取消订阅事件 * @param {string} eventType 事件类型 * @param {function} handler 要移除的事件处理函数 */ unsubscribe(eventType, handler) { if (!this.events[eventType]) { return; } const index = this.events[eventType].indexOf(handler); if (index !== -1) { this.events[eventType].splice(index, 1); } } /** * 发布事件 * @param {string} eventType 事件类型 * @param {...any} args 传递给处理函数的参数 */ publish(eventType, ...args) { if (!this.events[eventType]) { return; } // 遍历执行所有订阅了该事件的处理函数 this.events[eventType].forEach(handler => { try { handler.apply(null, args); // 使用 apply 传递参数 } catch (e) { console.error(`Error in event handler for ${eventType}:`, e); } }); } } // 示例用法 const emitter = new EventEmitter(); // 定义处理函数 const handler1 = (data) => { console.log('Handler 1 received:', data); }; const handler2 = (msg, value) => { console.log('Handler 2 received:', msg, value); }; // 订阅事件 emitter.subscribe('dataUpdate', handler1); emitter.subscribe('message', handler2); emitter.subscribe('message', (msg) => { console.log('Handler 3 (anonymous) received:', msg); }); // 发布事件 console.log('Publishing dataUpdate...'); emitter.publish('dataUpdate', { userId: 123, status: 'online' }); console.log('\nPublishing message...'); emitter.publish('message', 'Hello subscribers!', 42); // 取消订阅 emitter.unsubscribe('dataUpdate', handler1); console.log('\nPublishing dataUpdate again...'); emitter.publish('dataUpdate', { newData: true }); // Handler 1 不再收到通知 ``` ### 4. 关键注意事项 (Key Considerations) * **内存泄漏:** 如果事件监听器注册后没有被移除(如在组件销毁时未 `unsubscribe`),可能会导致内存泄漏。 * **事件名称冲突:** 在大型应用中,需要有避免事件名称冲突的策略,例如使用命名空间。 * **错误处理:** 一个处理函数中的错误不应影响其他处理函数的执行。需要有try-catch等机制来隔离错误。 * **同步/异步:** 上述示例是同步的。如果需要异步通知,可以在 `publish` 方法中使用 `setTimeout` 或其他异步机制。 * **`this` 上下文:** 在事件处理函数中,`this` 的指向可能需要通过 `bind` 或箭头函数来确保其正确性。 ### 5. 参考资料 (References) * **MDN Web Docs - EventTarget:** [https://developer.mozilla.org/en-US/docs/Web/API/EventTarget](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget) (虽然是浏览器API,但它是发布订阅模式的典型实现之一) * **Wikipedia - Observer pattern:** [https://en.wikipedia.org/wiki/Observer_pattern](https://en.wikipedia.org/wiki/Observer_pattern) * **掘金 - 发布订阅模式在前端的应用与实现:** (搜索此类文章,选择评价高、内容权威的技术博客) - *请注意:此处不提供具体链接,需用户自行根据需求查找可靠的业界博客。* --- 原文链接与附件索引 https://developer.mozilla.org/en-US/docs/Web/API/EventTarget https://developer.mozilla.org/en-US/docs/Web/API/EventTarget https://en.wikipedia.org/wiki/Observer_pattern https://en.wikipedia.org/wiki/Observer_pattern