簡單理解觀察者模式(pub/sub)在前端中的應用

概念

觀察者模式被普遍地應用於JavaScript客戶端編程中。全部的瀏覽器事件(mouseover,keypress等)都是使用觀察者模式的例子。這種模式的另外一個名字叫「自定義事件」,意思是這些事件是被編寫出來的,和瀏覽器觸發的事件相對。它還有另一個名字叫「訂閱者/發佈者」模式。
使用這個模式的最主要目的就是促進代碼觸解耦。在觀察者模式中,一個對象訂閱另外一個對象的指定活動並獲得通知,而不是調用另外一個對象的方法。訂閱者也被叫做觀察者,被觀察的對象叫做發佈者或者被觀察者(譯註:subject,不知道如何翻譯,第一次的時候譯爲「主體」,第二次譯時以爲不妥,仍是直接叫被觀察者好了)。當一個特定的事件發生的時候,發佈者會通知(調用)全部的訂閱者,同時還可能以事件對象的形式傳遞一些消息。 (摘自javascript 模式一書)javascript

維基百科定義:在軟件架構中,發佈-訂閱是一種消息範式,消息的發送者(稱爲發佈者)不會將消息直接發送給特定的接收者(稱爲訂閱者)。而是將發佈的消息分爲不一樣的類別,無需瞭解哪些訂閱者(若是有的話)可能存在。一樣的,訂閱者能夠表達對一個或多個類別的興趣,只接收感興趣的消息,無需瞭解哪些發佈者(若是有的話)存在。html

理解觀察者模式:

摘自 http://www.cnblogs.com/tugenh...java

JS傳統事件就是一個觀察者模式,之因此要有觀察者模式,是由於有時候和傳統事件無關的事件,好比:2個或者更多模塊的直接通訊問題,好比說我有個index.html頁面,我有不少JS文件,好比:git

a.js: function a(){}; b.js: function b(){}; c.js function c(){}; 等等。後面還有許多這樣的JS,那麼我要在index.html初始化這些函數的話,我須要這樣調用a();b();c()等等,也就是說頁面調用的時候 我要這樣調用,增長了依賴性,我要知道有多少個函數要這樣初始化調用,可是若是咱們如今用觀察者模式就不須要知道有哪些訂閱者,好比一個模塊(或者多個模塊)訂閱了一個主題(或者事件),另外一個模塊發佈這個主題時候,訂閱這個主題模塊就能夠執行了,觀察者主要讓訂閱者與發佈者解耦,發佈者不須要知道哪些模塊訂閱了這個主題,它只管發佈這個主題就能夠了,一樣訂閱者也無需知道那個模塊會發布這個主題,它只管訂閱這個主題就能夠了。這樣2個模塊(或更多模塊)就實現了關聯了。而不須要和上面代碼同樣,我要知道哪些模塊要初始化,我要怎樣初始化。這只是一個簡單的列子解釋觀察者模式要使用在什麼地方,我也看過不少博客關於這方面的資料,可是不少人寫博客只是講了如何實現觀察者模式及觀察者模式的好處,並無講咱們何時該使用觀察者模式,因此我列舉了上面的列子,就是多個不一樣業務模塊須要相互關聯的時候,能夠使用觀察者模式。就比如requireJS,seaJS,KISSY解決依賴的問題同樣(好比A依賴於B,B依賴於C,只要一個解決入口文件,其餘都會異步加載出來同樣)。也就是說各個模塊之間的關聯性能夠使用觀察者模式來設計。github

代碼實現:在該篇博客(https://www.cnblogs.com/Lucky...編程

(function(){
    var Event = {
        on: function (type, handler) {
            if (!this.handlers) {
                // this.handlers = {};    
                Object.defineProperty(this, "handlers", {
                    value: {},
                    enumerable: false,  // important 避免拷貝Event時污染handlers
                    configurable: true,
                    writable: true
                })
            }   
            if (typeof this.handlers[type] === 'undefined') {
                this.handlers[type] = [];
            }
            this.handlers[type].push(handler);
        },
        emit: function (eventName) {
            if (this.handlers[arguments[0]] instanceof Array) {
                var handlers = this.handlers[arguments[0]];
                for (var i=0; i<handlers.length; i++) {
                    handlers[i](arguments[1].message);
                }
            }
        },
        unsubscribe: function (type, handler) {
            if (this.handlers[type] instanceof Array) {
                var handlers = this.handlers[type];
                for (var i=0; i<handlers.length; i++) {
                    if (handlers[i] === handler) {
                        break;
                    }
                }
                handlers.splice(i, 1);
            }
        }
    };
  
    Event.on('test', function (message) {
        console.log(message);
    });
    Event.on('test', function () {
        console.log('test');
    });

    Event.emit('test', {message: 'hello world'});

    var person1 = {
        sayHello: function (message) {
            console.log(message);
        }
    };
    var person2 = {
        sayHello: function (message) {
            console.log(message);
        }
    };
    Object.assign(person1, Event);
    Object.assign(person2, Event);
    person1.on('call1', person1.sayHello);
    person2.on('call2', person2.sayHello);
    person1.emit('call1', {message:'person1 is calling call1.'}); // 'person1 is calling call1.' 
    person1.emit('call2', {message:'p111'}); //  no output
    person2.emit('call1', {message:'p222'}); //  no output
    person2.emit('call2', {message:'person2 is calling call2.'}); // 'person2 is calling call2'
    person1.unsubscribe('call1', person1.sayHello);
    person1.emit('call1', {message:'person1 is calling call1 again.'}); // no output
    person1.on('call1', person1.sayHello);
    person1.emit('call1', {message:'person1 is calling call1 again.'}); // person1 is calling call1 again.

    let paper = Object.assign({}, Event);
    let mike = {
        haveABreak: function (news) {
            let msg = 'Mike just read news: ' + news;
            console.log('-------------');
            console.log(msg);
        }
    };
    let tom = {
        haveABreak: function (news) {
            let msg = 'Tom just read news: ' + news;
            console.log(msg);
            console.log('-------------');
        }
    };
    paper.on('dailyNews', mike.haveABreak);
    paper.on('dailyNews', tom.haveABreak);
    paper.emit('dailyNews', {message: 'Coding is getting better.'});

})()

可在個人github上下載上述代碼(https://github.com/ylzsmallsu...),幷包含JavaScript模式中觀察者模式例子。瀏覽器

此外,觀察者模式還可用於實現數據綁定

思想:使用數據特性來爲 HTML 代碼進行綁定,全部被綁定在一塊兒的 JavaScript 對象和 DOM 元素都會訂閱一個 PubSub 對象。只要 JavaScript 對象或者一個 HTML 輸入元素監聽到數據的變化時,就會觸發綁定到 PubSub 對象上的事件,從而其餘綁定的對象和元素都會作出相應的變化。架構

參考文章

本篇文章主要是對查閱的幾篇文檔集中梳理,有興趣的能夠翻閱以上參考文章。異步

相關文章
相關標籤/搜索