亚洲中字慕日产2020,大陆极品少妇内射AAAAAA,无码av大香线蕉伊人久久,久久精品国产亚洲av麻豆网站

資訊專欄INFORMATION COLUMN

從觀察者模式到手寫EventEmitter源碼

cocopeak / 1226人閱讀

摘要:觀察者模式觀察者模式廣泛的應(yīng)用于語(yǔ)言中,瀏覽器事件如鼠標(biāo)單擊,鍵盤事件都是該模式的例子??梢钥吹?,這就是觀察者模式的訂閱方法實(shí)現(xiàn)。小結(jié)通過(guò)創(chuàng)建可觀察的對(duì)象,當(dāng)發(fā)生一個(gè)感興趣的事件時(shí)可將該事件通告給所有觀察者,從而形成松散的耦合。

觀察者模式

觀察者模式(observer)廣泛的應(yīng)用于javascript語(yǔ)言中,瀏覽器事件(如鼠標(biāo)單擊click,鍵盤事件keyDown)都是該模式的例子。設(shè)計(jì)這種模式背后的主要原因是促進(jìn)形成低耦合,在這種模式中不是簡(jiǎn)單的對(duì)象調(diào)用對(duì)象,而是一個(gè)對(duì)象“訂閱”另一個(gè)對(duì)象的某個(gè)活動(dòng),當(dāng)對(duì)象的活動(dòng)狀態(tài)發(fā)生了改變,就去通知訂閱者,而訂閱者也稱為觀察者。

報(bào)紙訂閱

生活中就像是去報(bào)社訂報(bào)紙,你喜歡讀什么報(bào)就去報(bào)社去交錢訂閱,當(dāng)發(fā)布了新報(bào)紙的時(shí)候,報(bào)社會(huì)向所有訂閱了報(bào)紙的每一個(gè)人發(fā)送一份,訂閱者就可以接收到。

我們可以利用這個(gè)例子來(lái)使用javascript來(lái)模擬一下。假設(shè)有一個(gè)發(fā)布者Jack,它每天出版報(bào)紙雜志,訂閱者Tom將被通知任何時(shí)候發(fā)生的新聞。

Jack要有一個(gè)subscribers屬性,它是一個(gè)數(shù)組類型,訂閱的行為將會(huì)按順序存放在這個(gè)數(shù)組中,而通知意味著調(diào)用訂閱者對(duì)象的某個(gè)方法。因此,當(dāng)用戶Tom訂閱信息的時(shí)候,該訂閱者要向Jack的subscribe()提供他的一個(gè)方法。當(dāng)然也可以退訂,我不想再看報(bào)紙了,就調(diào)用unsubscribe()取消訂閱。

一個(gè)簡(jiǎn)單的觀察者模式應(yīng)有以下成員:

subscribes 一個(gè)數(shù)組

subscribe() 將訂閱添加到數(shù)組里

unsubscribe() 把訂閱從數(shù)組中移除

publish() 迭代數(shù)組,調(diào)用訂閱時(shí)的方法

這個(gè)模式中還需要一個(gè)type參數(shù),用于區(qū)分訂閱的類型,如有的人訂閱的是娛樂(lè)新聞,有的人訂閱的是體育雜志,使用此屬性來(lái)標(biāo)記。

我們使用簡(jiǎn)單的代碼來(lái)實(shí)現(xiàn)它:

var Jack = {
    subscribers: {
        "any": []
    },
    //添加訂閱
    subscribe: function (type = "any", fn) {
        if (!this.subscribers[type]) {
            this.subscribers[type] = [];
        }
        this.subscribers[type].push(fn); //將訂閱方法保存在數(shù)組里
    },
    //退訂
    unsubscribe: function (type = "any", fn) {
        this.subscribers[type] =
            this.subscribers[type].filter(function (item) { 
                return item !== fn;
            }); //將退訂的方法從數(shù)組中移除
    },
    //發(fā)布訂閱
    publish: function (type = "any", ...args) {
        this.subscribers[type].forEach(function (item) { 
            item(...args);    //根據(jù)不同的類型調(diào)用相應(yīng)的方法
        });
    }
};

以上就是一個(gè)最簡(jiǎn)單的觀察者模式的實(shí)現(xiàn),可以看到代碼非常的簡(jiǎn)單,核心原理就是將訂閱的方法按分類存在一個(gè)數(shù)組中,當(dāng)發(fā)布時(shí)取出執(zhí)行即可。

下面使用Tom來(lái)訂報(bào):

var Tom = {
    readNews: function (info) {
        console.log(info);
    }
};

//Tom訂閱Jack的報(bào)紙
Jack.subscribe("娛樂(lè)", Tom.readNews);
Jack.subscribe("體育", Tom.readNews);

//Tom 退訂娛樂(lè)新聞:
Jack.unsubscribe("娛樂(lè)", Tom.readNews);

//發(fā)布新報(bào)紙:
Jack.publish("娛樂(lè)", "S.H.E演唱會(huì)驚喜登臺(tái)")
Jack.publish("體育", "歐國(guó)聯(lián)-意大利0-1客負(fù)葡萄牙");

運(yùn)行結(jié)果:

歐國(guó)聯(lián)-意大利0-1客負(fù)葡萄牙
觀察者模式的實(shí)際應(yīng)用

可以看到觀察者模式將兩個(gè)對(duì)象的關(guān)系變得十分松散,當(dāng)不需要訂閱關(guān)系的時(shí)候刪掉訂閱的語(yǔ)句即可。那么在實(shí)際應(yīng)用中有哪些地方使用了這個(gè)模式呢?

events模塊

node.js的events是一個(gè)使用率很高的模塊,其它原生node.js模塊都是基于它來(lái)完成的,比如流、HTTP等,我們可以手寫一版events的核心代碼,看看觀察者模式的實(shí)際應(yīng)用。

events模塊的功能就是一個(gè)事件綁定,所有繼承自它的實(shí)例都具備事件處理的能力。首先它是一個(gè)類,我們寫出它的基本結(jié)構(gòu):

function EventEmitter() {
    //私有屬性,保存訂閱方法
    this._events = {};
}

//默認(rèn)最大監(jiān)聽(tīng)數(shù)
EventEmitter.defaultMaxListeners = 10;

module.exports = EventEmitter;

下面我們一個(gè)個(gè)將events的核心方法實(shí)現(xiàn)。

on方法

首先是on方法,該方法用于訂閱事件,在舊版本的node.js中是addListener方法,它們是同一個(gè)函數(shù):

EventEmitter.prototype.on =
    EventEmitter.prototype.addListener = function (type, listener, flag) {
        //保證存在實(shí)例屬性
        if (!this._events) this._events = Object.create(null);

        if (this._events[type]) {
            if (flag) {//從頭部插入
                this._events[type].unshift(listener);
            } else {
                this._events[type].push(listener);
            }

        } else {
            this._events[type] = [listener];
        }
        //綁定事件,觸發(fā)newListener
        if (type !== "newListener") {
            this.emit("newListener", type);
        }
    };

因?yàn)橛衅渌宇愋枰^承自EventEmitter,因此要判斷子類是否存在_event屬性,這樣做是為了保證子類必須存在此實(shí)例屬性。而flag標(biāo)記是一個(gè)訂閱方法的插入標(biāo)識(shí),如果為"true"就視為插入在數(shù)組的頭部。可以看到,這就是觀察者模式的訂閱方法實(shí)現(xiàn)。

emit方法
EventEmitter.prototype.emit = function (type, ...args) {
    if (this._events[type]) {
        this._events[type].forEach(fn => fn.call(this, ...args));
    }
};

emit方法就是將訂閱方法取出執(zhí)行,使用call方法來(lái)修正this的指向,使其指向子類的實(shí)例。

once方法
EventEmitter.prototype.once = function (type, listener) {
    let _this = this;

    //中間函數(shù),在調(diào)用完之后立即刪除訂閱
    function only() {
        listener();
        _this.removeListener(type, only);
    }
    //origin保存原回調(diào)的引用,用于remove時(shí)的判斷
    only.origin = listener;
    this.on(type, only);
};

once方法非常有趣,它的功能是將事件訂閱“一次”,當(dāng)這個(gè)事件觸發(fā)過(guò)就不會(huì)再次觸發(fā)了。其原理是將訂閱的方法再包裹一層函數(shù),在執(zhí)行后將此函數(shù)移除即可。

off方法
EventEmitter.prototype.off =
    EventEmitter.prototype.removeListener = function (type, listener) {

        if (this._events[type]) {
        //過(guò)濾掉退訂的方法,從數(shù)組中移除
            this._events[type] =
                this._events[type].filter(fn => {
                    return fn !== listener && fn.origin !== listener
                });
        }
    };

off方法即為退訂,原理同觀察者模式一樣,將訂閱方法從數(shù)組中移除即可。

prependListener方法
EventEmitter.prototype.prependListener = function (type, listener) {
    this.on(type, listener, true);
};

此方法不必多說(shuō)了,調(diào)用on方法將標(biāo)記傳為true(插入訂閱方法在頭部)即可。

以上,就將EventEmitter類的核心方法實(shí)現(xiàn)了。

小結(jié)

通過(guò)創(chuàng)建“可觀察的”對(duì)象,當(dāng)發(fā)生一個(gè)感興趣的事件時(shí)可將該事件通告給所有觀察者,從而形成松散的耦合。

部分實(shí)例參考《JavaScript模式》作者:Stoyan Stefanov

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/97694.html

相關(guān)文章

  • 解析nodeJS模塊源碼 親手打造基于ES6的察者系統(tǒng)

    摘要:為指定事件注冊(cè)一個(gè)單次監(jiān)聽(tīng)器,即監(jiān)聽(tīng)器最多只會(huì)觸發(fā)一次,觸發(fā)后立刻解除該監(jiān)聽(tīng)器。移除指定事件的某個(gè)監(jiān)聽(tīng)器,監(jiān)聽(tīng)器必須是該事件已經(jīng)注冊(cè)過(guò)的監(jiān)聽(tīng)器。返回指定事件的監(jiān)聽(tīng)器數(shù)組。如何創(chuàng)建空對(duì)象我們已經(jīng)了解到,是要來(lái)儲(chǔ)存監(jiān)聽(tīng)事件監(jiān)聽(tīng)器數(shù)組的。 毫無(wú)疑問(wèn),nodeJS改變了整個(gè)前端開(kāi)發(fā)生態(tài)。本文通過(guò)分析nodeJS當(dāng)中events模塊源碼,由淺入深,動(dòng)手實(shí)現(xiàn)了屬于自己的ES6事件觀察者系統(tǒng)。千萬(wàn)不...

    csRyan 評(píng)論0 收藏0
  • JavaScript 發(fā)布-訂閱模式

    摘要:發(fā)布訂閱模式訂閱者把自己想訂閱的事件注冊(cè)到調(diào)度中心,當(dāng)發(fā)布者發(fā)布該事件到調(diào)度中心,也就是該事件觸發(fā)時(shí),由調(diào)度中心統(tǒng)一調(diào)度訂閱者注冊(cè)到調(diào)度中心的處理代碼。 發(fā)布-訂閱模式,看似陌生,其實(shí)不然。工作中經(jīng)常會(huì)用到,例如 Node.js EventEmitter 中的 on 和 emit 方法;Vue 中的 $on 和 $emit 方法。他們都使用了發(fā)布-訂閱模式,讓開(kāi)發(fā)變得更加高效方便。 一...

    13651657101 評(píng)論0 收藏0
  • Node 之 Event 模塊

    摘要:為什么把叫做集合而不能稱為嚴(yán)格意義上的對(duì)象,來(lái)看這個(gè)集合的構(gòu)造函數(shù)可以見(jiàn)得,是與處于同一層級(jí)的而非是繼承自,所以說(shuō)由實(shí)例出來(lái)的對(duì)象更加的純凈,并沒(méi)有諸如等方法,更像是一個(gè)集合。 寫在前面 事件的編程方式具有輕量級(jí)、松耦合、只關(guān)注事務(wù)點(diǎn)等優(yōu)勢(shì),在瀏覽器端,有著自己的一套DOM事件機(jī)制,其中含包括這諸如事件冒泡,事件捕獲等;然而Node的事件機(jī)制沒(méi)有事件冒泡等,其原理就是設(shè)計(jì)模式中的觀察者...

    mrli2016 評(píng)論0 收藏0
  • 循序漸進(jìn)教你實(shí)現(xiàn)一個(gè)完整的node的EventEmitter模塊

    摘要:本文從的的使用出發(fā),循序漸進(jìn)的實(shí)現(xiàn)一個(gè)完整的模塊。移除指定事件的某個(gè)監(jiān)聽(tīng)器,監(jiān)聽(tīng)器必須是該事件已經(jīng)注冊(cè)過(guò)的監(jiān)聽(tīng)器的別名移除所有事件的所有監(jiān)聽(tīng)器,如果指定事件,則移除指定事件的所有監(jiān)聽(tīng)器。返回指定事件的監(jiān)聽(tīng)器數(shù)組。 node的事件模塊只包含了一個(gè)類:EventEmitter。這個(gè)類在node的內(nèi)置模塊和第三方模塊中大量使用。EventEmitter本質(zhì)上是一個(gè)觀察者模式的實(shí)現(xiàn),這種模式可...

    sunsmell 評(píng)論0 收藏0
  • EventEmitter命令式 JavaScript class 聲明函數(shù)式的華麗轉(zhuǎn)身

    摘要:典型和改造挑戰(zhàn)了解事件發(fā)布訂閱系統(tǒng)實(shí)現(xiàn)思想,我們來(lái)看一段簡(jiǎn)單且典型的基礎(chǔ)實(shí)現(xiàn)上面代碼,實(shí)現(xiàn)了一個(gè)類我們維護(hù)一個(gè)類型的,對(duì)不同事件的所有回調(diào)函數(shù)進(jìn)行維護(hù)。方法對(duì)指定事件進(jìn)行回調(diào)函數(shù)存儲(chǔ)方法對(duì)指定的觸發(fā)事件,逐個(gè)執(zhí)行其回調(diào)函數(shù)。 showImg(https://segmentfault.com/img/remote/1460000014287200); 新書終于截稿,今天稍有空閑,為大家奉...

    hsluoyz 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<