發(fā)布訂閱
發(fā)布訂閱是一種設(shè)計(jì)模式井厌。
發(fā)布訂閱:
一個(gè)對(duì)象能夠發(fā)布一個(gè)事件秒赤,然后你能訂閱這個(gè)事件。
設(shè)計(jì)模式:
是一種可用的痹愚、別人用過覺得還行的一種寫代碼套路富岳,比如用閉包去隱藏變量。
需求:
兩個(gè)模塊間形成事件交互拯腮。
模塊A和B 窖式,中間值e叫做事件中心。
A有什么事通知B就觸發(fā)一下e,B就訂閱一下动壤。
B有什么事通知A就出發(fā)一下e萝喘,A就訂閱一下
兩者不直接交流,由e牽線搭橋。
分析
我們需要實(shí)現(xiàn)一個(gè)對(duì)象阁簸,這個(gè)對(duì)象就是通信的中介eventHub,實(shí)現(xiàn)三個(gè)接口爬早。
eventHub這個(gè)對(duì)象要有三個(gè)函數(shù)
const eventHub = {
on:()=>{} //監(jiān)聽事件
emit:()=>{} //trigger 觸發(fā)事件
off:()=>{} //取消監(jiān)聽事件
}
//在任意地方使用on來監(jiān)聽事件
eventHub.on('click',f1)
//用off取消監(jiān)聽事件
eventHub.off('click',f1)
//可以在用戶點(diǎn)擊按鈕 或三秒鐘之后觸發(fā)它
setTimeout(()=>{
eventHub.emit('click','frank')
},3000)
實(shí)現(xiàn)
寫函數(shù)首先搞清楚輸入輸出是什么
on 接收兩個(gè)參數(shù) 事件類型和函數(shù),不需要返回值。
off同理启妹。
emit接收事件類型和參數(shù)筛严。依舊不需要返回值
它們分別做什么
on:如果你監(jiān)聽了一個(gè)事件,我就把你放到任務(wù)隊(duì)列中饶米。
off; 就是把它從任務(wù)隊(duì)列中拿出來桨啃。
emit就是調(diào)用隊(duì)列中的任務(wù)。
任務(wù)隊(duì)列是什么
- 任務(wù):函數(shù)就是任務(wù)檬输。
- 隊(duì)列:
需要在觸發(fā)時(shí)執(zhí)行這個(gè)任務(wù)照瘾,比如我監(jiān)聽兩次click。
eventHub.on('click',f1)
eventHub.on('click',f2)
那我f1 和 f2調(diào)用的順序褪猛,先調(diào)用f1再調(diào)用f2网杆。
先進(jìn)先出就是隊(duì)列,所以我們需要一個(gè)隊(duì)列伊滋。
但是實(shí)際上我們會(huì)有很多隊(duì)列:如click隊(duì)列碳却,右鍵隊(duì)列 左鍵多列等等。
所以我們要實(shí)現(xiàn)一個(gè)映射關(guān)系笑旺,用map 叫哈希表昼浦。JS中只能用對(duì)象來表示哈希表。
所以我們實(shí)現(xiàn)一個(gè)map隊(duì)列表筒主。
用到的其他東西
- 防御性編程:如果name不存在我們可以初始化一個(gè)关噪。
on:(name,fn)=>{
寫法1:直接push
eventHub.map[name].push(fn)可以 但是需要防御性編程 name有可能是空的
寫法2:防御性編程
if(eventHub.map[name]===undefined){
eventHub.map[name] = []
}
eventHub.map[name].push(fn)
寫法3:簡寫
eventHub.map[name] = eventHub.map[name] || []
eventHub.map[name].push(fn)
//這里不能亂用別名,因?yàn)榱硗夂瘮?shù)是這個(gè)是寫乌妙。q = []就出現(xiàn)問題了
}
- 短路:可以少寫else
if(!q){return}
if(index<0) {return}
- 別名:減少代碼使兔。alias設(shè)計(jì)模式,叫縮寫也叫別名。
const q = eventHub.map[name]
-
forEach和map:
用map和forEach遍歷基本一樣 但是map有返回值 forEach沒有 - 代碼
const eventHub = {
map:{
},
on:(name,fn)=>{
//入隊(duì)
eventHub.map[name] = eventHub.map[name] || []
eventHub.map[name].push(fn)
} ,
emit:(name,data)=>{
const q = eventHub.map[name]
if(!q){return}
q.map((f)=>{f.call(null,data)})//遍歷調(diào)用一遍藤韵。 這f的this是空虐沥。
} ,
off:(name,fn)=>{
const q = eventHub.map[name]
if(!q){return}
const index = q.indexOf(fn)
if(index<0) {return}
q.splice(index,1)
}
}
- 測試
eventHub.on('click',console.log)
eventHub.on('click',console.error)
setTimeout(()=>{
eventHub.emit('click','frank')
},3000)
-
完美
發(fā)布訂閱
面試有可能會(huì)問到 如何實(shí)現(xiàn)once:()=>{}
只監(jiān)聽一次,自動(dòng)結(jié)束泽艘。