手寫js發(fā)布訂閱模式以及觀察者模式

在js中發(fā)布訂閱模式和觀察者模式都是非常常用的兩種設(shè)計(jì)模式。兩者也及其相似,初學(xué)者很容易搞混了兩種設(shè)計(jì)模式,下面通過手寫的方式帶大家完整的認(rèn)識(shí)這兩種設(shè)計(jì)模式以及他們之間的區(qū)別

發(fā)布訂閱模式

先大致的分析一下什么是發(fā)布訂閱模式

可以參考一下微信的公眾號(hào)訂閱功能,用戶訂閱了這個(gè)公眾號(hào),公眾號(hào)有了新消息就會(huì)推送給用戶。在這樣一個(gè)流程中,大致就可以分出三個(gè)角色來 用戶充當(dāng)?shù)木褪?strong>訂閱者而公眾號(hào)則是發(fā)布者,那第三個(gè)角色是誰呢? 第三個(gè)角色當(dāng)然就是公眾號(hào)的數(shù)據(jù)來源啦,這個(gè)數(shù)據(jù)來源可以看成一個(gè)中轉(zhuǎn)站。他并不關(guān)心誰訂閱了消息,訂閱了什么消息,他要做的就是存下訂閱者的聯(lián)系方式,當(dāng)對(duì)應(yīng)的消息被發(fā)布了他就通知訂閱者。

知道了他的大概思想之后就可以勾勒出代碼的大致輪廓了

//發(fā)布訂閱容器
let observeEmit = {
 eventList: {},//裝載訂閱者所訂閱的事件
//訂閱函數(shù)
 on(key, fn) {},
//發(fā)布函數(shù)
 emit(key, paylod) {}
}

上述代碼中eventList就是用來存儲(chǔ)訂閱者的聯(lián)系方式的,on方法用來提供給用戶進(jìn)行訂閱emit用來發(fā)布

這兩個(gè)函數(shù)的具體實(shí)現(xiàn)如下

        //訂閱函數(shù)
        on(key, fn) {
            /**
             * key  被訂閱的內(nèi)容的key值
             * fn 訂閱者傳入的訂閱事件
             */
            if (!this.eventList[key]) {
                this.eventList[key] = []
            }
            this.eventList[key].push(fn)
        },
        //發(fā)布函數(shù)
        emit(key, paylod) {
                for (let i = 0; i < this.eventList[key].length; i++) {
                    if (this.eventList){
                        this.eventList[key][i](paylod)
                    }
                }
        },

至此,一個(gè)簡單的發(fā)布訂閱模式就寫好了,主要就是在訂閱的時(shí)候?qū)ey以及對(duì)應(yīng)的回調(diào)函數(shù)存入容器里,然后在發(fā)布某個(gè)消息的時(shí)候去觸發(fā)所有的對(duì)應(yīng)的key的回調(diào)函數(shù)。
下面看看如何使用吧

    observeEmit.on('msg', function (content) {
        console.log('訂閱msg的人收到最新消息:', content)
    })
   observeEmit.emit('msg', '我觸發(fā)了發(fā)布,請(qǐng)問訂閱者收到了嗎')

這還只是一個(gè)最簡單版的,我們還可以對(duì)他進(jìn)行功能擴(kuò)展

比如,提供只訂閱一次的api

        //只訂閱一次
        once(key, fn) {
            this.on(key, function (content) {
                fn(content)
                this.off(key, fn)
            })
        },
        //取消訂閱
        off(key, fn) {
            if (this.eventList[key]) {
                for (let i = 0; i < this.eventList[key].length; i++) {
                    if (this.eventList[key][i] === fn) {
                        this.eventList[key][i].splice(i, 1)
                    }
                }
            }
        }

如果先發(fā)布了消息,用戶再訂閱,我們還可以將事件存儲(chǔ)起來,然后在有人訂閱的時(shí)候?qū)⑺l(fā)布出去

在對(duì)象中定義一個(gè)離線事件的屬性
        offLineEventList: {},//離線事件棧,如果當(dāng)前發(fā)布的事件暫無訂閱者,就先保存到此事件棧中,等下次有人訂閱了再將此消息發(fā)布給他

改造一下on和emit
//訂閱函數(shù)
        on(key, fn) {
            /**
             * key  被訂閱的內(nèi)容的key值
             * fn 訂閱者傳入的訂閱事件
             */
            if (!this.eventList[key]) {
                this.eventList[key] = []
            }
            this.eventList[key].push(fn)
            //如果離線數(shù)據(jù)棧中有當(dāng)前訂閱的key,那就將之前發(fā)布的消息發(fā)送給訂閱者
            if (this.offLineEventList[key]) {
                this.emit(key, this.offLineEventList[key])
            }
        },
        //發(fā)布函數(shù)
        emit(key, paylod) {
            console.log(arguments)
            if (this.eventList[key]) {
                for (let i = 0; i < this.eventList[key].length; i++) {
                    if (this.eventList){
                        this.eventList[key][i](paylod)
                    }
                }
            } else {
                //如果沒人訂閱過這個(gè)事件,就將本次發(fā)布內(nèi)容先存在離線事件棧內(nèi)
                this.offLineEventList[key] = paylod
            }
        },

這樣一個(gè)較為完善的發(fā)布訂閱模式就寫好啦。
接下來我們就來看看觀察者模式又是怎么樣的吧

觀察者模式

觀察者模式其實(shí)可以簡單的分為兩個(gè)角色,一個(gè)觀察者和一個(gè)被觀察者。由被觀察者主動(dòng)通知觀察者,告訴他我發(fā)生了變化,你可以做你想做的事情了

下面簡單實(shí)現(xiàn)一下

    //觀察者類
    class Observe {
        constructor(name) {
           this.name = name
        }
        //觀察的數(shù)據(jù)發(fā)生了變化,觸發(fā)函數(shù)
        update(payload){
            console.log(`${this.name}觀察的數(shù)據(jù)發(fā)生了變化:${payload}`)
        }
    }
    //被觀察的類
    class Subject {
        constructor() {
            this.observeList = []
        }
        //添加觀察者進(jìn)觀察者數(shù)組
        addObserve(observe){
            this.observeList.push(observe)
        }
        //發(fā)生變更通知觀察者
        notify(payload){
            this.observeList.forEach(observe=>{
                observe.update(payload)
            })
        }
    }
    const subject = new Subject()
    const stu1 = new Observe('小明')
    const stu2 = new Observe('小虹')
    subject.addObserve(stu1)
    subject.addObserve(stu2)
    subject.notify('測試一下')

發(fā)布訂閱模式中有三個(gè)角色,而觀察者模式中只有兩個(gè)角色。
發(fā)布訂閱模式由中轉(zhuǎn)站收集訂閱者的訂閱消息,然后發(fā)布者通過中轉(zhuǎn)站來通知到對(duì)應(yīng)的訂閱者,
而觀察者模式則是有被觀察者主動(dòng)收集他自身的觀察者,然后自身發(fā)生變化后主動(dòng)的去通知觀察者,也就是調(diào)用觀察者的某個(gè)函數(shù)。兩者的概念非常相似,但是實(shí)現(xiàn)卻大有不同

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容