成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

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

cocopeak / 1183人閱讀

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

觀察者模式

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

報紙訂閱

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

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

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

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

subscribes 一個數(shù)組

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

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

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

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

我們使用簡單的代碼來實現(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)的方法
        });
    }
};

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

下面使用Tom來訂報:

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

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

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

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

運行結(jié)果:

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

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

events模塊

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

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

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

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

module.exports = EventEmitter;

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

on方法

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

EventEmitter.prototype.on =
    EventEmitter.prototype.addListener = function (type, listener, flag) {
        //保證存在實例屬性
        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);
        }
    };

因為有其它子類需要繼承自EventEmitter,因此要判斷子類是否存在_event屬性,這樣做是為了保證子類必須存在此實例屬性。而flag標(biāo)記是一個訂閱方法的插入標(biāo)識,如果為"true"就視為插入在數(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方法來修正this的指向,使其指向子類的實例。

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

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

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

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

        if (this._events[type]) {
        //過濾掉退訂的方法,從數(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);
};

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

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

小結(jié)

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

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

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

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/97694.html

相關(guān)文章

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

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

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

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

    13651657101 評論0 收藏0
  • Node 之 Event 模塊

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

    mrli2016 評論0 收藏0
  • 循序漸進教你實現(xiàn)一個完整的node的EventEmitter模塊

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

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

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

    hsluoyz 評論0 收藏0

發(fā)表評論

0條評論

cocopeak

|高級講師

TA的文章

閱讀更多
最新活動
閱讀需要支付1元查看
<