發布-訂閱
模式也是經典的設計模式
之一,它在前端很多地方都有應用,比如javascript事件池
,Vue的$on、$off
,nodejs的events模塊和socket通信
等等都有應用,也是前端面試比較火熱的考點之一,接下來給大家詳細介紹下發布-訂閱模式
發布-訂閱模式定義了對象間的一種一對多的依賴關系
,當一個對象的狀態發生變化時,所有依賴它的對象都將得到通知
。在JavaScript開發中,我們一般用事件模型來替代傳統的發布-訂閱模式
要手寫一個簡單的發布訂閱模式,其實現思路如下:
- 先初始化一個
events對象
- 調用
on
方法時,將事件名稱eventName
和監聽函數fn
存入events對象
中 - 調用
emit
方法時,通過事件名稱eventName
從events對象
中取出對應的回調并執行 off
方法:通過事件名稱eventName
找出events對象
對應的監聽函數并清除once
方法:被emit
觸發一次后就立即調用off
方法移除監聽,也就是調用once
傳入的監聽函數只會執行一次
代碼不多,所以直接上完整代碼
class EventEmitter {constructor() {this.events = {}}on(eventName, fn) {if (!this.events[eventName]) {this.events[eventName] = []}this.events[eventName].push(fn)return this}once(eventName, fn) {const func = (...args) => {this.off(eventName, func)fn.apply(this, args)}this.on(eventName, func)return this}emit(eventName, ...args) {if (!this.events[eventName]) return thisthis.events[eventName].forEach(fn => {fn.apply(this, args)});return this}off(eventName, fn) {if (!this.events[eventName]) return thisif (typeof fn === 'function') {this.events[eventName] = this.events[eventName].filter((f) => f !== fn)return this}this.events[eventName] = nullreturn this}
}
const events = new EventEmitter();events.on('event1', () => {console.log('event1', '第一個監聽函數')
})
events.on('event1', () => {console.log('event1', '第二個監聽函數')
})
events.emit('event1')const fn1 = () => {console.log('event2', '第一個監聽函數')
}
const fn2 = () => {console.log('event2', '第二個監聽函數')
}
events.on('event2', fn1)
events.on('event2', fn2)
events.off('event2', fn1);// 打印結果:
// event1 第一個監聽函數
// event1 第二個監聽函數// 上面代碼有疑問請閱讀下面代碼,這部分解答了上面讀者的疑問
const events = new EventEmitter();// event1部分
events.on('event1', () => {console.log('event1', '第一個監聽函數')
})
events.on('event1', () => {console.log('event1', '第二個監聽函數')
})
events.emit('event1')// event2部分
const fn1 = () => {console.log('event2', '第一個監聽函數')
}
const fn2 = () => {console.log('event2', '第二個監聽函數')
}
events.on('event2', fn1)
events.off('event2', fn1);
events.on('event2', fn2)
events.emit('event2')
- 可以廣泛應用于異步編程中,這是一種替代傳遞回調函數的方案;
- 可以取代對象之間硬編碼的通知機制,一個對象不用再顯示地調用一個對象的接口