再次理解訂閱釋出模式
阿新 • • 發佈:2021-10-07
之前的總結:設計模式之釋出訂閱模式
為什麼會有這種設計模式
這裡有個很好的回答:https://segmentfault.com/q/1010000002487388
簡單的基於物件的訂閱釋出模式
function EventEmitter() { this._event = {} } EventEmitter.prototype.on= function (type, handle) { this._event[type] = this._event[type] || [] this._event[type].push(handle) } EventEmitter.prototype.remove = function (type, handle) { var index = (this._event[type] || []).indexOf(handle) if(index !== -1) { this._event[type].splice(index, 1) } } EventEmitter.prototype.emit = function (type, data) { (this._event[type] || []).forEach(function (handle) { handle(data) }) } var test = new EventEmitter(); var handle1 = function (data) { console.log(data[0]) } var handle2 = function () { console.log(data[1]) } test.on('fetchData', handle1) test.on('fetchData', handle2) test.remove('fetchData', handle2) test.emit('fetchData', [1,2,3]) // 1
常見的使用場景:當我們在ajax的非同步資料請求結束後,emit一個事件,外部可以通過監聽這個事件執行不同的操作
原生web api基於DOM元素的釋出訂閱
<!DOCTYPE html> <html lang="en"> <head> <title>Document</title> </head> <body> <div id="ele"></div> <script> // 基於物件的訂閱/釋出 // 基於DOM元素的事件訂閱/釋出 var event = new CustomEvent('add', { detail: 'hello' }); var handle = function (e) { console.log('handle:' + e.detail); } var handle2 = function (e) { console.log('handle2:' + e.detail); } ele.addEventListener('add', handle) ele.addEventListener('add', handle2) ele.dispatchEvent(event); // handle:hello handle2:hello ele.removeEventListener('add', handle2); ele.dispatchEvent(event); // handle:hello </script> </body> </html>
使用ES6實現的釋出訂閱
http://zchange.cn/posts/332959902.html
class Event { constructor() { this._subscribers = new Map(); this.__index = 0; } /** * 將訂閱者資訊存入list * @param {String} eventName 事件名稱 * @param {fn} callback 訂閱回撥 * 通過Map來存取對應的訂閱者 * 監聽同一個主體,下一次的不會覆蓋上一次的監聽 * 返回訂閱者名稱,和對應的下標,可供後面銷燬 */ subscribe(eventName, callback) { if (typeof eventName !== 'string' || typeof callback !== 'function') { throw new Error('parameter error') } if (!this._subscribers.has(eventName)) { this._subscribers.set(eventName,new Map()); } // 訂閱同一個主題通過_index不會覆蓋上一次。 this._subscribers.get(eventName).set(++this._index,callback); return [eventName, this._index] } on(eventName, callback) { return this.subscribe(eventName, callback); } /** * 釋出資訊 * @param {String} eventName 訂閱者名稱 * @param {any} args 引數 */ emit(eventName, ...args) { if(this._subscribers.has(eventName)){ const eveMap = this._subscribers.get(eventName); eveMap.forEach((map) =>map(...args)); }else{ throw new Error(`The subscription parameter ${eventName} does not exist`) } } /** * 銷燬對應訂閱者 * @param {String|Object} event */ destroy(event) { if (typeof event === 'string') { // 直接銷燬對應訂閱者 if (this._subscribers.has(event)) { this._subscribers.delete(event) } } else if (typeof event === 'object') { // 通過訂閱者名詞和下標,銷燬其中某一個訂閱者 const [eventName, key] = event; this._subscribers.get(eventName).delete(key); } } /** * 清除所有訂閱者 */ remove() { this._subscribers.clear(); } } const $event = new Event(); const ev1 = $event.on('aa', (...agrs) => { console.log(...agrs); console.log(111); }) const ev2 = $event.on('aa', (...agrs) => { console.log(...agrs); console.log(222); }) setTimeout(() => { $event.emit('aa', '1', '2'); $event.destroy(); $event.remove(); }, 500)
原生web api實現依賴於dom元素,即釋出者和訂閱者都是dom元素,因此使用場景有限。基於物件的實現方式常用於跨元件之間的資料傳遞,可以更好的解耦釋出者和訂閱者之間的聯絡。
部落格: https://blog.86886.wangGitHub: https://github.com/wmui