JavaScript設計模式整理

寫在前面

設計模式是程序員通識知識,熟練掌握並使用各類設計模式,能夠體現一個程序員的工程開發水平。我花了幾天時間,重溫並整理了30多種設計模式,以JavaScript爲示例語言。下面我會列出一些經常使用的設計模式說明及示例,更全面的內容見:https://github.com/yzsunlei/javascript-design-modejavascript

什麼是設計模式?

一個模式就是一個可重用的方案,可應用於在軟件設計中的常見問題。另外一種解釋就是一個咱們如何解決問題的模板 - 那些能夠在許多不一樣的狀況裏使用的模板。html

設計模式的分類:

建立型設計模式:
一、簡單工廠模式
二、工廠方法模式
三、抽象工廠模式
四、建造者模式
五、原型模式
六、單例模式 java

結構型設計模式:
七、外觀模式
八、適配器模式
九、代理模式
十、裝飾者模式
十一、橋接模式
十二、組合模式
1三、享元模式git

行爲型設計模式:
1四、模板方法模式
1五、觀察者模式
1六、狀態模式
1七、策略模式
1八、職責鏈模式
1九、命令模式
20、訪問者模式
2一、中介者模式
2二、備忘錄模式
2三、迭代器模式
2四、解釋器模式程序員

技巧型設計模式:
2五、鏈模式
2六、委託模式
2七、數據訪問對象模式
2八、節流模式
2九、簡單模板模式
30、惰性模式
3一、參與者模式
3二、等待者模式github

架構型設計模式:
3三、同步模塊模式
3四、異步模塊模式
3五、Widget模式
3六、MVC模式
3七、MVP模式
3八、MVVM模式canvas

  • 備註:該分類借鑑於《JavaScript設計模式-張容銘》

工廠方法模式:

經過對產品類的抽象使其建立業務主要負責用於建立多類產品的實例。設計模式

// 安全模式建立的工廠類
var Factory = function(type, content) {
    if (this instanceof Factory) {
        // 保證是經過new進行建立的
        var s = new this[type](content);
        return s;
    } else {
        return new Factory(type, content);
    }
};

// 工廠原型中設置建立全部類型數據對象的基類
Factory.prototype = {
    Java: function(content) {

    },
    Php: function(content) {

    },
    JavaScript: function(content) {

    }
};

原型模式:

用原型實例指向建立對象的類,使用於建立新的對象的類共享原型對象的屬性以及方法。瀏覽器

// 圖片輪播類
var LoopImages = function(imgArr, container) {
    this.imagesArray = imgArr;
    this.container = container;
};

LoopImages.prototype = {
    // 建立輪播圖片
    createImage: function() {
        console.log("LoopImages createImage function");
    },
    // 切換下一張圖片
    changeImage: function() {
        console.log("LoopImages changeImage function");
    }
};

// 上下滑動切換類
var SliderLoopImg = function(imgArr, container) {
    // 構造函數繼承圖片輪播類
    LoopImages.call(this, imgArr, container);
};
SliderLoopImg.prototype = new LoopImages();
// 重寫繼承的「切換下一張圖片」方法
SliderLoopImg.prototype.changeImage = function() {
    console.log("SliderLoopImg changeImage function");
};

單例模式:

又稱單體模式,是隻容許實例化一次的對象類。緩存

// 惰性
var LarySingle = (function() {
    // 單例實例引用
    var _instance = null;
    // 單例
    function Single() {
        // 這裏定義私有屬性和方法
        return {
            publicMethod: function() {},
            publicProperty: "1.0"
        };
    }
    // 獲取單例對象接口
    return function() {
        // 若是未建立單例將建立單例
        if(!_instance){
            _instance = Single();
        }
        // 返回單例
        return _instance;
    };
})();

外觀模式:

爲一組複雜的子系統接口提供一個更高級的統一接口,經過這個接口使得對子系統接口的訪問更容易。

function addEvent(dom, type, fn) {
    // 對於支持DOM2級事件處理程序addEventListener方法的瀏覽器
    if (dom.addEventListener) {
        dom.addEventListener(type, fn, false);
    } else if (dom.attachEvent) {
        // 對於不支持addEventListener方法但支持attchEvent方法的瀏覽器
        dom.attachEvent("on" + type, fn);
    } else {
        // 對於不支持addEventListener方法,也不支持attchEvent方法,但支持「on」+事件名的瀏覽器
        dom["on" + type] = fn;
    }
}

裝飾者模式:

在不改變原對象的基礎上,經過對其進行包裝拓展(添加屬性或方法)使原對象能夠知足用戶更復雜需求。

var decorator = function (input, fn) {
    // 獲取事件源
    var input = document.getElementById(input);
    // 若事件源已經綁定事件
    if (typeof input.click === 'function') {
        // 緩存事件源原有回調函數
        var oldClickFn = input.click;
        // 爲事件源定義新的事件
        input.click = function () {
            // 事件源原有回調函數
            oldClickFn();
            // 執行事件源新增回調函數
            fn();
        }
    } else {
        // 事件源未綁定事件,直接爲事件源添加新增回調函數
        input.onclick = fn;
    }
}

觀察者模式:

又稱發佈-訂閱者模式或消息機制,定義一種依賴關係,解決了主體對象與觀察者之間功能的耦合。

var Observer = (function () {
    var __messages = {};
    return {
        // 註冊消息
        register: function (type, fn) {
            if (typeof __messages[type] === 'undefined') {
                __messages[type] = [fn];
            } else {
                __messages[type].push(fn);
            }
        },
        // 發佈消息
        fire: function (type, args) {
            if (!__messages[type])
                return;
            var events = {
                type: type,
                args: args || {}
            };
            var i = 0;
            var len = __messages[type].length;
            for (; i < len; i++) {
                __messages[type][i].call(this, events);
            }
        },
        // 移除消息
        remove: function (type, fn) {
            if (__messages[type] instanceof Array) {
                var i = __messages[type].length - 1;
                for (; i >= 0; i--) {
                    __messages[type][i] == fn && __messages[type].splice(i, 1);
                }
            }
        }
    }
})();

狀態模式:

當一個對象的內部狀態發生改變時,會致使其行爲的改變,這看起來像是改變了對象。

// 狀態對象
var ResultState = function () {
    var States = {
        state0: function () {
            console.log("第一種狀況");
        },
        state1: function () {
            console.log("第二種狀況");
        },
        state2: function () {
            console.log("第三種狀況");
        },
        state3: function () {
            console.log("第四種狀況");
        }
    };

    function show(result) {
        States['state' + result] && States['state' + result]();
    }

    return {
        show: show
    }
}();

命令模式:

將請求與實現解耦並封裝成獨立對象,從而使不一樣的請求對客戶端的實現參數化。

// 繪圖命令
var CanvasCommand = (function () {
   var canvas = document.getElementById('canvas');
   var ctx = canvas.getContext('2d');
   var Action = {
       fillStyle: function (c) {
           ctx.fillStyle = c;
       },
       fillRect: function (x, y, w, h) {
           ctx.fillRect(x, y, w, h);
       },
       strokeStyle: function (c) {
           ctx.strokeStyle = c;
       },
       strokeRect: function (x, y, w, h) {
           ctx.strokeRect(x, y, w, h);
       },
       fillText: function (text, x, y) {
           ctx.fillText(text, x, y);
       },
       beginPath: function () {
           ctx.beginPath();
       },
       moveTo: function (x, y) {
           ctx.moveTo(x, y);
       },
       lineTo: function (x, y) {
           ctx.lineTo(x, y);
       },
       arc: function (x, y, r, begin, end, dir) {
           ctx.arc(x, y ,r, begin, end, dir);
       },
       fill: function () {
           ctx.fill();
       },
       stroke: function () {
           ctx.stroke();
       }
   };
   return {
       excute: function (msg) {
           if (!msg)
               return;
           if (msg.length) {
               for (var i = 0, len = msg.length; i < len; i++) {
                   arguments.callee(msg[i]);
               }
           } else {
               msg.param = Object.prototype.toString.call(msg.param) === "[object Array]" ? msg.param : [msg.param];
               Action[msg.command].apply(Action, msg.param);
           }
       }
   }
})();

迭代器模式:

在不暴露對象內部結構的同時,能夠順序的訪問聚合對象內部的元素。

// 迭代器
var Iterator = function (items, container) {
    var container = container && document.getElementById(container) || document;
    var items = container.getElementsByTagName(items);
    var len = items.length;
    var idx = 0;
    var splice = [].splice();

    return {
        first: function () {},
        second: function () {},
        pre: function () {},
        next: function () {},
        get: function () {},
        dealEach: function () {},
        dealItem: function () {},
        exclusive: function () {}
    }
};

鏈模式:

經過在對象方法中將當前對象返回,實現對同一個對象多個方法的鏈式調用。

var A = function (selector) {
    return new A.fn.init(selector);
};
A.fn = A.prototype = {
    constructor: A,
    init: function (selector) {
        console.log(this.constructor);
    }
};
A.fn.init.prototype = A.fn;

節流模式:

對重複的業務邏輯進行節流控制,執行最後一次操做並取消其餘操做,以提升性能。

var throttle = function () {
    var isClear = arguments[0];
    var fn;
    if (typeof isClear === 'boolean') {
        fn = arguments[1];
        fn.__throttleID && clearTimeout(fn.__throttleID);
    } else {
        fn = isClear;
        param = arguments[1];
        var p = extend({
            context: null,
            args: [],
            time: 30
        }, param);
        arguments.callee(true, fn);
        fn.__throttleID = setTimeout(function () {
            fn.apply(p.context, p.args);
        }, p.time);
    }
}

參與者模式:

在特定的做用域中執行給定的函數,並將參數原封不動的傳遞。

// 函數綁定
function bind(fn, context) {
    return function () {
        return fn.apply(context, arguments);
    }
}

// 函數柯里化
function curry(fn) {
    var Slice = [].slice;
    var args = Slice.call(arguments, l);
    return function () {
        var addArgs = Slice.call(arguments);
        var allArgs = args.concat(addArgs);
        return fn.apply(null, allArgs);
    }
}

參考資料

相關文章
相關標籤/搜索