JavaScript工廠模式和訂閱模式

樑音發表於2017-10-31

設計模式的好處:

  1. 程式碼規範
// 例如表單驗證,兩個 input ,一個使用者名稱,一個密碼
// 通常做法是
function checkUser(){
    //.....
}
function checkPassword(){
    //.....
}
// 問題:
// 這是兩個全域性物件,而這兩個方法屬於一個表單的驗證

// 所以這應該是一個表單物件,起碼應該這麼寫
// 物件封裝,但是注意全域性物件
var checkObj = {
    checkUser: function() {
        //.....
    },
    checkPassword: function() {
        //.....
    }
}

// 問題:
// 物件過多
// 解決:閉包

(function(){
    //.....
})()

工廠模式

// ajax 為例
var xhr = new XMLHttpRequest(); // IE6不相容XMLHttpRequest物件,需要使用ActiveXObject("Microsoft.XMLHTTP")
xhr.onreadystatechange = function(){

}
xhr.open();
xhr.send(null);
// 使用工廠模式
// 好處:低耦合,高內聚
var XMLHTTPFactory = function() {
    var XMLHTTP = null;
    if (window.XMLHttpRequest()){
        XMLHTTP = new XMLHttpRequest();
    } else {
        XMLHTTP = new ActiveXObject("Microsoft.XMLHTTP");
    }
    return XMLHTTP;
}
// 使用
var xhr = XMLHTTPFactory();

訂閱者模式

// 訂閱者:買家
// 釋出者:賣家
var showObj = {}; // 定義釋出者
showObj.list = []; // 儲存訂閱者
// 增加訂閱者,訂閱訊息
showObj.listen = function (fn) { // fn回撥函式,輸出你想輸出的內容
    showObj.list.push(fn);
}
showObj.trigger = function () {
    for (var i = 0, fn; fn = this.list[i++];) {
        fn.apply(this, arguments);
    }
}

showObj.listen(function (color, size) {
    console.log("顏色是" + color);
    console.log("尺碼是" + size);
})

showObj.listen(function (color, size) {
    console.log("再次輸出顏色是" + color);
    console.log("再次輸出尺碼是" + size);
})

showObj.trigger("白色", 40);
showObj.trigger("黑色", 20);

// 你會發現輸出了兩遍,這是為什麼呢?
// 原因就在於showObj.list.push(fn)
// 那麼如何使不同的訂閱者檢視到屬於自己的訂閱呢

使用一個key區分

var showObj = {}; // 定義釋出者
showObj.list = []; // 儲存訂閱者
// 增加訂閱者,訂閱訊息
showObj.listen = function (key, fn) { // 在訂閱的時候給一個標識
    if (!this.list[key]) {
        this.list[key] = [];
    }
    showObj.list[key].push(fn);
}
showObj.trigger = function () { // 釋出的時候認識知道訂閱的訊息分別釋出給誰
    // 取到訊息名稱
    var key = Array.prototype.shift.call(arguments);
    // 對應的回撥函式
    var fns = this.list[key];
    if (!fns || fns.length === 0) {
        return;
    }
    for (var i = 0, fn; fn = fns[i++];) {
        fn.apply(this, arguments);
    }
}

showObj.listen("white", function (color, size) {
    console.log("小愛同學訂閱的顏色是" + color);
    console.log("小愛同學訂閱的尺碼是" + size);
})

showObj.listen("black", function (color, size) {
    console.log("小冰同學訂閱的顏色是" + color);
    console.log("小冰同學訂閱的尺碼是" + size);
})

showObj.trigger("white", "白色", 40);
showObj.trigger("black", "黑色", 20);
// 但是這樣的寫的話適用性太差,所以我們需要對其進行封裝。

封裝

var event = {
    list: [],
    listen: function (key, fn) { // 在訂閱的時候給一個標識
        if (!this.list[key]) {
            this.list[key] = [];
        }
        this.list[key].push(fn);
    },
    trigger: function () { // 釋出的時候認識知道訂閱的訊息分別釋出給誰
        // 取到訊息名稱
        var key = Array.prototype.shift.call(arguments);
        // 對應的回撥函式
        var fns = this.list[key];
        if (!fns || fns.length === 0) {
            return;
        }
        for (var i = 0, fn; fn = fns[i++];) {
            fn.apply(this, arguments);
        }
    },
    // 移除方法(取消訂閱)
    remove: function (key, fn) {
        var fns = this.list[key];
        // 沒有訂閱過
        if (!fns) {
            return false;
        }
        // 回撥函式為空
        if (!fn) {
            fn && (fns.length = 0);
        } else {
            for (var i = fns.length - 1; i >= 0; i++) {
                var _fn = fns[i];
                if (_fn === fn) {
                    fns.splice(i, 1);//刪除訂閱者的回撥函式
                }
            }
        }
    }
};

var initEvent = function (obj) {
    for (var i in event) {
        obj[i] = event[i]
    }
}
var showObj = {};// 讓任何一個普通物件都擁有釋出訂閱功能
initEvent(showObj);

showObj.listen("white", fn1 = function (color, size) {
    console.log("小愛同學訂閱的顏色是" + color);
    console.log("小愛同學訂閱的尺碼是" + size);
})

showObj.listen("black", fn2 = function (color, size) {
    console.log("小冰同學訂閱的顏色是" + color);
    console.log("小冰同學訂閱的尺碼是" + size);
})

showObj.trigger("white", "白色", 40);
showObj.trigger("black", "黑色", 20);


相關文章