訂閱-發佈模式是設計模式中的一種應用場景比較多的模式,定義了一種對象間一對N的依賴關係(注意是一對N),當一個對象的狀態發生改變時,全部依賴於它的對象都將獲得更新。
訂閱發佈模式相較於觀察者模式能夠說是一種改進,其比觀察者模式耦合性低。javascript
觀察上圖能夠看到訂閱發佈模式大體能夠分爲三個角色,分別爲發佈者、管理中心(姑且這麼稱呼)、訂閱者。管理中心就像一箇中轉站,維繫着訂閱者和發佈者之間的關係,中轉着各類不一樣發佈者發佈的信息,並將其準確無誤的派發到每種訂閱者手中,有點兒像快遞小哥兒哈(更像快遞櫃...)java
下面來說如何用js實現一個簡單的訂閱發佈模式
首先咱們要設想下管理中心的功能:設計模式
有了功能咱們就要開始設想如何實現了,這裏咱們假設每一個發佈者只有發佈一種消息,首先在有發佈者A,發佈者B,發佈者C...的狀況下就須要進行身份(key)辨認了,咱們先設置一個處理隊列,分別存放每一個發佈者要發佈的信息。數組
let queue = {
'發佈者A的key': [
function 訂閱者a要這麼作(){},
function 訂閱者b要這麼作(){}
]
}複製代碼
而後咱們已經存儲了發佈者A和訂閱者a、b之間的關係了,下面咱們還要讓A發佈的時候分別執行a、b的訂閱方法。因此基本代碼爲:函數
const bus = function (){
let queue = {}, emit, on, off
on = (key, fn) =>{
if(!queue[key]){
queue[key] = []
}
queue[key].push(fn)
}
emit = (key, ...args) => {
let fns = queue[key]
if(!fns || !fns.length){
return
}
fns.map(_ => {
_(...args) })
}
return {
$on: on,
$emit: emit
}
}複製代碼
稍微解釋下:$on方法即爲收集依賴的方法,經過其能夠將發佈者與訂閱者們的關係放處處理隊列中,而後$emit方法爲發佈者發佈更新的方法,當發佈者發佈更新的時候,對應key的函數組便會依次執行達到更新每一個訂閱者的目的。ui
上述代碼已經基本實現了功能,美中不足就是不能將發佈者和訂閱者「分手」,有合必有分,因此咱們還須要一個分手的方法。
大致思路:訂閱者經過發佈者的key來找到擬分手的發佈者,而後提供本身的處理方法名字,而後在處理隊列中刪除,兩不相欠,若是發佈者想徹底退出還要把此key對應的處理方法都清空。spa
完整代碼:設計
// 這裏的bus就是一個管理中心,全部訂閱者發佈者能夠經過其進行交流
const bus = ()=> {
let queue = {}, emit, on, off
on = (key, fn) =>{
if(!queue[key]){
queue[key] = []
}
queue[key].push(fn)
}
emit = (key, ...args) => {
let fns = queue[key]
if(!fns || !fns.length){
return
}
fns.map(_ => {
_(...args)
})
}
off = (key, fn) => {
if(queue[key] === undefined || queue[key].length === 0){
return
}
if(fn !== undefined){
queue[key] = queue[key].filter(_ => _ !== fn)
} else{
queue[key] = []
}
}
return {
$on: on,
$emit: emit,
$off: off
}
}複製代碼
好了,這就是訂閱發佈模式的簡易實現,有興趣的小夥伴能夠在控制檯試下如下代碼~code
let bu = new bus()
function fn1(val){
console.log(`fn1: ${val}`)
}
function fn2(val){
console.log(`fn2: ${val}`)
}
function fn3(val){
console.log(`fn3: ${val}`)
}
// 第一個監聽
bu.$on('test', fn1)
// 第二個監聽
bu.$on('test', fn2)
// 發佈消息
bu.$emit('test', 'blablabla')
// 解綁第一個
bu.$off('test', fn1)
// 第三個監聽
bu.$on('test', fn3)
// 發佈消息
bu.$emit('test', 'hahaha')
// 所有解綁
bu.$off('test')
// 發佈消息
bu.$emit('test', '???')複製代碼
若有錯誤,還望你們指出,謝謝🙏~cdn