国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

Javascript(七)發(fā)布-訂閱模式

megatron / 819人閱讀

摘要:發(fā)布訂閱的作用發(fā)布訂閱模式可以廣泛應(yīng)用與異步編程中,這是一種替代傳統(tǒng)回調(diào)函數(shù)的方案。發(fā)布訂閱模式讓兩個(gè)對(duì)象松散耦合地聯(lián)系在一起,雖然不清楚彼此的細(xì)節(jié),但不影響它們之間的通信。

發(fā)布-訂閱模式又叫觀察者模式,它定義與對(duì)象間的一種一對(duì)多的關(guān)系,當(dāng)一個(gè)對(duì)象的狀態(tài)發(fā)生改變時(shí),所有依賴與它的對(duì)象都將得到通知。
發(fā)布訂閱的作用

發(fā)布-訂閱模式可以廣泛應(yīng)用與異步編程中,這是一種替代傳統(tǒng)回調(diào)函數(shù)的方案。

比如訂閱ajax的succ,error等事件,只需訂閱事件發(fā)生點(diǎn),而無(wú)需關(guān)注異步運(yùn)行期間狀態(tài)

發(fā)布-訂閱可以取代對(duì)象之間硬編碼的通信機(jī)制,一個(gè)對(duì)象不再顯式地調(diào)用另外一個(gè)對(duì)象的某個(gè)接口。

發(fā)布-訂閱模式讓兩個(gè)對(duì)象松散耦合地聯(lián)系在一起,雖然不清楚彼此的細(xì)節(jié),但不影響它們之間的通信。

當(dāng)有新的訂閱者出現(xiàn)時(shí),發(fā)布者的代碼不需要任何修改;同樣發(fā)布者改變時(shí),也不會(huì)影響到之前的訂閱者。

發(fā)布-訂閱模式的通用實(shí)現(xiàn)
var event = {
    //緩存列表,存放訂閱者的回調(diào)函數(shù)
    clientList: [],
    //增加訂閱者, key:事件名,fn:回調(diào)函數(shù)
    listen: function(key, fn){
        if(!this.clientList[key]){
            this.clientList[key] = []
        }
        this.clientList[key].push(fn)
    },
    //發(fā)布消息
    trigger: function(){
        // 取出消息類型
        var key = Array.prototype.shift.call(arguments)
            // 取出消息類型對(duì)應(yīng)的回調(diào)函數(shù)集合
            fns = this.clientList[key]
        // 如果沒(méi)有訂閱該消息,則返回
        if(!fns || fns.length === 0) {
            return false
        }
        for(var i = 0, fn; fn = fns[i++];) {
            fn.apply(this, arguments)
        }
    },
    //取消訂閱
    remove: function(key, fn) {
        var fns = this.clientList[key]
        // 如果對(duì)應(yīng)的消息沒(méi)人訂閱,直接返回
        if(!fns) {    
            return false
        }
        // 如果沒(méi)有傳入回調(diào)函數(shù),表示取消key對(duì)應(yīng)消息的所有訂閱
        if(!fn){    
            fns && (fns.length = 0)
        } else {
            for(var l = fns.length - 1; l >= 0; l--) {
                var _fn = fns[l]
                if (_fn === fn) {
                    fns.splice(l, 1)
                }
            }
        }
    }
}

var salesOffices = {}
// 給對(duì)象安裝發(fā)布-訂閱功能
var installEvent = function(obj){
    for(var i in event){
        obj[i] = event[i]
    }
}
installEvent(salesOffices)


salesOffices.listen("squareMeter88", fn1 = function(price){
    console.log("價(jià)格1:"+price)
})

salesOffices.listen("squareMeter87", fn2 = function(price){
    console.log("價(jià)格2:"+price)
})    

salesOffices.trigger("squareMeter88", 20000) // 價(jià)格1: 20000

salesOffices.remove("squareMeter88")

salesOffices.trigger("squareMeter88", fn1)  // 打印結(jié)果為空

其實(shí)在這個(gè)發(fā)布-訂閱模式中還存在兩個(gè)問(wèn)題:

給對(duì)象多帶帶添加其實(shí)是一種資源浪費(fèi)

發(fā)布者與訂閱者存在一定耦合性,至少要知道salesOffices這個(gè)對(duì)象

所以接下來(lái)實(shí)現(xiàn)一個(gè)全局的發(fā)布-訂閱對(duì)象

var Event = (function(){
    var clientList = {},
        listten,
        trigger,
        remove;
    listen = function(key, fn){
        if(!clientList[key]){
            clientList[key] = []
        }
        clientList[key].push(fn)
    }
    trigger = function(){
        var key = Array.prototype.shift.call(arguments)
            fns = clientList[key]
        if(!fns || fns.length === 0) {
            return false
        }
        for(var i = 0, fn; fn = fns[i++];) {
            fn.apply(this, arguments)
        }
    },
    remove = function(key, fn) {
        var fns = clientList[key]
        if(!fns) {    
            return false
        }
        if(!fn){    
            fns && (fns.length = 0)
        } else {
            for(var l = fns.length - 1; l >= 0; l--) {
                var _fn = fns[l]
                if (_fn === fn) {
                    fns.splice(l, 1)
                }
            }
        }
    }    
    return {
        listen,
        trigger,
        remove
    }
})()
Event.listen("squareMeter88", fn1 = function(price){ // 訂閱消息
    console.log("價(jià)格1:"+price)
})
Event.trigger("squareMeter88", 20000) // 價(jià)格1: 20000
必須先訂閱在發(fā)布嗎

我們所了解的訂閱發(fā)布都是訂閱者必須先訂閱一個(gè)消息,隨后才能接受到發(fā)布者發(fā)布的消息。如果順序反過(guò)來(lái),發(fā)布者先發(fā)布一條消息,而此時(shí)沒(méi)有訂閱者,無(wú)疑消息會(huì)消失。

探討下這種需求

在某些情況下,我們需要先講這條消息保存下來(lái),等到有對(duì)象訂閱它的時(shí)候,再重新把消息發(fā)布給訂閱者,如果QQ中的離線消息,離線消息先被保存在服務(wù)器中,接收人下次登陸之后,可以從新接收到這條消息。

再假如一個(gè)商城系統(tǒng)中,獲取到用戶信息后才能渲染導(dǎo)航模塊,無(wú)疑這是一個(gè)異步的過(guò)程,不能保證ajax何時(shí)返回。所以我們需要我們發(fā)布訂閱對(duì)象擁有先發(fā)布后訂閱的能力。

如何實(shí)現(xiàn)呢

為了滿足這種需要,我們要建立一個(gè)存放離線事件的堆棧,當(dāng)事件發(fā)布的時(shí)候,如果此時(shí)還沒(méi)有訂閱者來(lái)訂閱這個(gè)事件,我們暫時(shí)把發(fā)布事件的動(dòng)作包裹在一個(gè)函數(shù)里,這些包裹函數(shù)被存入堆棧中,等到終于有對(duì)象來(lái)訂閱此事件,我們將遍歷堆棧并且依次執(zhí)行這些包裝函數(shù),也就是重新發(fā)布里面的事件。當(dāng)然離線事件的生命周期只有一次,就像QQ未讀消息只會(huì)被重新閱讀一次。

命名沖突

全局的訂閱-發(fā)布對(duì)象里只有一個(gè)clientList來(lái)存放消息名和回調(diào)函數(shù),我們都通過(guò)它來(lái)訂閱和發(fā)布各種消息,久而久之,難免會(huì)出現(xiàn)事件名沖突的情況,所以需要給Event對(duì)象提供創(chuàng)建命名空間的功能。

具體實(shí)現(xiàn)
var Event = (function() {

    var Event,
        _default = "default";

    Event = function() {
        var _listen,
            _trigger,
            _remove,
            _shift = [].shift,
            _unshift = [].unshift,
            namespaceCache = {},
            _create,
            each = function(arr, fn) {
                var ret;
                for (var i = 0, l = arr.length; i < l; i++) {
                    var n = arr[i];
                    ret = fn.call(n, i, n);
                }

                return ret;
            };

        _listen = function(key, fn, cache) {
            if (!cache[key]) {
                cache[key] = [];
            }
            cache[key].push(fn);
        };

        _remove = function(key, cache, fn) {
            if (cache[key]) {
                if (fn) {
                    for (var i = cache[key].length - 1; i >= 0; i--) {
                        if (cache[key][i] === fn) { 
                            // 刪除訂閱回調(diào)函數(shù)
                            cache[key].splice(i, 1);
                        }
                    }
                }
                else {
                    cache[key] = [];
                }
            }
        };

        _trigger = function() {
            var cache = _shift.call(arguments);
            var key = _shift.call(arguments);
            var args = arguments;
            var _this = this;
            var stack = cache[key];

            if (!stack || !stack.length) {
                return;
            }

            return each(stack, function() {
                return this.apply(_this, args);
            })
        };

        _create = function(namespace) {
            namespace = namespace || _default;
            var cache = {};
            var offlineStack = []; //離線事件
            var ret = {
                listen: function(key, fn, last) {
                    _listen(key, fn, cache);

                    if (offlineStack === null) {
                        return;
                    }

                    if (last === "last") {
                        offlineStack.length && offlineStack.pop()();
                    }
                    else {
                        each(offlineStack, function() {
                            this();
                        });
                    }

                    offlineStack = null;
                },
                one: function(key, fn, last) {
                    _remove(key, cache);
                    this.listen(key, fn, last);
                },
                remove: function(key, fn) {
                    _remove(key, cache, fn);
                },
                trigger: function() {
                    var fn,
                        args,
                        _this = this;

                    _unshift.call(arguments, cache);
                    args = arguments;

                    fn = function() {
                        return _trigger.apply(_this, args);
                    };

                    if (offlineStack) {
                        return offlineStack.push(fn);
                    }

                    return fn();
                }
            };

            return namespace ?
                (namespaceCache[namespace] ?
                    namespaceCache[namespace] : namespaceCache[namespace] = ret)
                : ret;
        };

        return {
            create: _create,
            one: function(key, fn, last) {
                var event = this.create();
                event.one(key, fn, last);
            },
            remove: function(key, fn) {
                var event = this.create();
                event.remove(key, fn);
            },
            listen: function(key, fn, last) {
                var event = this.create();
                event.listen(key, fn, last);
            },
            trigger: function() {
                var event = this.create();
                event.trigger.apply(this, arguments);
            }
        }
    }();

    return Event;
}());

var fn1 = function(price) {
    console.log(price);
};

// 實(shí)例
Event.listen("squareMeter88", fn1);
Event.remove("squareMeter88", fn1);

Event.listen("squareMeter88", function(price) {
    console.log("fn2: " + price);
});


Event.trigger("squareMeter88", 20000);   // fn2: 20000
總結(jié) 優(yōu)點(diǎn)

時(shí)間與對(duì)象之間的解耦,應(yīng)用非常廣泛,既可以用在異步編程中,也可以幫助我們完成更松耦合的代碼編寫,從架構(gòu)上看,無(wú)論MVC還是MVVM都少不了發(fā)布-訂閱的參與

缺點(diǎn)

創(chuàng)建訂閱者本身要消耗一定時(shí)間和內(nèi)存,無(wú)論消息是否發(fā)生,一直存在內(nèi)存中。雖然發(fā)布-訂閱模式雖然可以弱化對(duì)象之間的聯(lián)系,但是過(guò)度使用,對(duì)象之間的必然聯(lián)系會(huì)被深埋,特別是多個(gè)訂閱者和發(fā)布者嵌套會(huì)造成難以追蹤的bug。

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

轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/108683.html

相關(guān)文章

  • JavaScript 模式》知識(shí)點(diǎn)小抄本(下)

    摘要:缺點(diǎn)不符合開(kāi)閉原則,如果要改東西很麻煩,繼承重寫都不合適。預(yù)防低水平人員帶來(lái)的風(fēng)險(xiǎn)。開(kāi)閉原則,高拓展性。這里的訂閱者稱為觀察者,而被觀察者稱為發(fā)布者,當(dāng)一個(gè)事件發(fā)生,發(fā)布者會(huì)發(fā)布通知所有訂閱者,并常常以事件對(duì)象形式傳遞消息。 介紹 最近開(kāi)始給自己每周訂個(gè)學(xué)習(xí)任務(wù),學(xué)習(xí)結(jié)果反饋為一篇文章的輸出,做好學(xué)習(xí)記錄。 這一周(02.25-03.03)我定的目標(biāo)是《JavaScript 模式》...

    xiguadada 評(píng)論0 收藏0
  • 【CuteJavaScript】Angular6入門項(xiàng)目(3.編寫服務(wù)和引入RxJS)

    摘要:發(fā)布通過(guò)回調(diào)方法向發(fā)布事件。觀察者一個(gè)回調(diào)函數(shù)的集合,它知道如何去監(jiān)聽(tīng)由提供的值。 本文目錄 一、項(xiàng)目起步 二、編寫路由組件 三、編寫頁(yè)面組件 1.編寫單一組件 2.模擬數(shù)據(jù) 3.編寫主從組件 四、編寫服務(wù) 1.為什么需要服務(wù) 2.編寫服務(wù) 五、引入RxJS 1.關(guān)于RxJS 2.引入RxJS 3.改造數(shù)據(jù)獲取方式 六、改造組件 1.添...

    RebeccaZhong 評(píng)論0 收藏0
  • JavaScript設(shè)計(jì)模式發(fā)布-訂閱模式(觀察者模式)-Part1

    摘要:設(shè)計(jì)模式與開(kāi)發(fā)實(shí)踐讀書筆記。發(fā)布訂閱模式又叫觀察者模式,它定義了對(duì)象之間的一種一對(duì)多的依賴關(guān)系。附設(shè)計(jì)模式之發(fā)布訂閱模式觀察者模式數(shù)據(jù)結(jié)構(gòu)和算法系列棧隊(duì)列優(yōu)先隊(duì)列循環(huán)隊(duì)列設(shè)計(jì)模式系列設(shè)計(jì)模式之策略模式 《JavaScript設(shè)計(jì)模式與開(kāi)發(fā)實(shí)踐》讀書筆記。 發(fā)布-訂閱模式又叫觀察者模式,它定義了對(duì)象之間的一種一對(duì)多的依賴關(guān)系。當(dāng)一個(gè)對(duì)象的狀態(tài)發(fā)生改變時(shí),所有依賴它的對(duì)象都將得到通知。 例...

    muzhuyu 評(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

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

0條評(píng)論

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