1. 程式人生 > >談談JS的觀察者模式(自定義事件)

談談JS的觀察者模式(自定義事件)

觀察者模式:
這是一種建立鬆散耦合程式碼的技術。它定義物件間 一種一對多的依賴關係,當一個物件的狀態發生改變時,所有依賴於它的物件都將得到通知。由主體和觀察者組成,主體負責釋出事件,同時觀察者通過訂閱這些事件來觀察該主體。主體並不知道觀察者的任何事情,觀察者知道主體並能註冊事件的回撥函式。

例子:

假如我們正在開發一個商城網站,網站裡有header頭部、nav導航、訊息列表、購物車等模組。這幾個模組的渲染有一個共同的前提條件,就是必須先用ajax非同步請求獲取使用者的登入資訊。這是很正常的,比如使用者的名字和頭像要顯示在header模組裡,而這兩個欄位都來自使用者登入後返回的資訊。這個時候,我們就可以把這幾個模組的渲染事件都放到一個數組裡面,然後待登入成功之後再遍歷這個陣列並且呼叫每一個方法。

function EventTarget(){     
    this.handlers = {}; 
} 
EventTarget.prototype = {     
    constructor: EventTarget,
    addHandler: function(type, handler){
         if (typeof this.handlers[type] == "undefined"){
              this.handlers[type] = [];
         }
         this.handlers[type].push(handler);
     }, 
    fire: function
(event){
if (!event.target){ event.target = this; } if (this.handlers[event.type] instanceof Array){ var handlers = this.handlers[event.type]; for (var i=0, len=handlers.length; i < len; i++){ handlers[i](event); } } }, removeHandler: function
(type, handler){
if (this.handlers[type] instanceof Array){ var handlers = this.handlers[type]; for (var i=0, len=handlers.length; i < len; i++){ if (handlers[i] === handler){ break; } } handlers.splice(i, 1); } } };

大概意思就是,建立一個事件管理器。handles是一個儲存事件處理函式的物件。

addHandle:是新增事件的方法,該方法接收兩個引數,一個是要新增的事件的型別,一個是這個事件的回撥函式名。呼叫的時候會首先遍歷handles這個物件,看看這個型別的方法是否已經存在,如果已經存在則新增到該陣列,如果不存在則先建立一個數組然後新增。

fire方法:是執行handles這個物件裡面的某個型別的每一個方法。

removeHandle:是相應的刪除函式的方法。

var Event = {
    // 通過on介面監聽事件eventName
    // 如果事件eventName被觸發,則執行callback回撥函式
    on: function (eventName, callback) {
        //我的程式碼
        if(!this.handles){
             this.handles={};    
        }      
       if(!this.handles[eventName]){
            this.handles[eventName]=[];
       }
       this.handles[eventName].push(callback);
    },
    // 觸發事件 eventName
    emit: function (eventName) {
        //你的程式碼
       if(this.handles[arguments[0]]){
           for(var i=0;i<this.handles[arguments[0]].length;i++){
               this.handles[arguments[0]][i](arguments[1]);
           }
       }
    }
};
var person1 = {};
var person2 = {};
Object.assign(person1, Event);
Object.assign(person2, Event);
person1.on('call1', function () {
    console.log('person1');
});
person2.on('call2', function () {
    console.log('person2');
});
person1.emit('call1'); // 輸出 'person1'
person1.emit('call2'); // 沒有輸出
person2.emit('call1'); // 沒有輸出
person2.emit('call2'); // 輸出 'person2'

完整版

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <script src='./mapbox-gl.js'></script>
<link href='./mapbox-gl.css' rel='stylesheet' />

<style>

.pageCover
            {
                width:100%;
                height:100%;
                position:absolute;
                z-index:10;
                background-color:#666;
                opacity:0.5;
                display:block;
            }
</style>

</head>
<body>
    <button id="button1">butn1</button>
    <button id="button2">butn2</button>

    <div class="pageCover"></div>
</body>
</html>

<script>


    var Event = {
    _listeners: {},    
    addEvent: function(type, fn) {
        if (typeof this._listeners[type] === "undefined") {
            this._listeners[type] = [];
        }
        if (typeof fn === "function") {
            this._listeners[type].push(fn);
        }    
        return this;
    },
    fireEvent: function(type) {
        var arrayEvent = this._listeners[type];
        if (arrayEvent instanceof Array) {
            for (var i=0, length=arrayEvent.length; i<length; i+=1) {
                if (typeof arrayEvent[i] === "function") {
                    arrayEvent[i]({type: type});    
                }
            }
        }    
        return this;
    },
    removeEvent: function(type, fn) {
        if (typeof type === "string" && arrayEvent instanceof Array) {
            if (typeof fn === "function") {
                for (var i=0, length=this._listeners[type].length; i<length; i+=1){
                    if (this._listeners[type][i] === fn){
                        this._listeners[type].splice(i, 1);
                        break;
                    }
                }
            } else {
                delete this._listeners[type];
            }
        }
        return this;
    }
};



    //test 

//  // 新增自定義事件

Event.addEvent("alert", fnAlert1 = function() {
    alert("第一個彈出!");
}).addEvent("alert", fnAlert2 = function() {
    alert("第二個彈出!");
});

// // 按鈕繫結事件,用來清除自定義事件
var elButton1 = document.getElementById("button1"),
    elButton2 = document.getElementById("button2");

elButton1.onclick = function() {
    Event.removeEvent("alert");
    alert("alert事件清除成功!");

    // 此時後一個按鈕臥底了,故隱藏
    elButton2.style.display = "none";
};

elButton2.onclick = function() {
    Event.removeEvent("alert", fnAlert1);
    alert("第一個alert清除成功!");
};



// 點選文件,觸發自定義事件
document.onclick = function(e) {
    e = e || window.event;
    var target = e.target || e.srcElement;

    // 如果文件點選元素標籤名不是input
    if (!target || !/input|pre/i.test(target.tagName)) {
        Event.fireEvent("alert");
    }
};

</script>