《JavaScript設計模式與開發實踐》讀書筆記

設計模式

單例模式

JS的單例模式有別於傳統面嚮對象語言的單例模式,js做爲一門無類的語言。使用全局變量的模式來實現單例模式思想。js裏面的單例又分爲普通單例和惰性單例,惰性單例指的是隻有這個實例在須要被建立的時候纔會被建立,建立後將始終保持這一個實例。算法

var getSingle = function (fn) {
    var result;
    return function () {
        return result || (result = fn.apply(this, arguments));
    }
};

策略模式

定義:定義一系列的算法,把它們一個個的封裝起來,而且使他們能夠相互替換。

策略模式至少由兩部分組成:第一個部分是一組策略類,用來封裝具體的算法,並負責計算過程;第二個部分是環境類,用於接受請求,並把請求委託給某一個策略類。策略模式,顧名思義就是指封裝一組組特定的算法,這些算法目的相同,用來實現不一樣條件下的特定要求。使用策略模式的優勢在於邏輯複用,代碼乾淨,減小多重條件判斷語句的使用。好比:設計模式

const strategies = {
    S: (salary) => {
        return salary * 4;
    },
    A: (salary) => {
        return salary * 3;
    },
    B: (salary) => {
        return salary * 2;
    }
}

const calculateBouns = (key, salary) => {
    return strategies[key] && strategies[key](salary);
}

calculateBouns('S', 10000);

代理模式

虛擬代理

虛擬代理在不改變原有函數(對象)的方法結構的前提下,定義新的對象,而且實現一樣的接口,給被代理函數賦予額外的行爲與邏輯,作一些過濾、合併、預處理等操做。api

var myImage = (function () {
    var imgNode = document.createElement('img');
    document.body.appendChild(imgNode);

    return function (src) {
        imgNode.src = src;
    }
})();

var proxyImage = (function () {
    var img = new Image;
    img.onload = function () {
        myImage(this.src);
    };

    return function (src) {
        myImage('loading.gif');
        img.src = src;
    };
})();

緩存代理

緩存代理能夠爲一些開銷大的運算結果提供暫時的緩存(適用純函數模式),在下次運算時,若是傳遞的參數和以前保持一致,則直接返回以前存儲的運算結果。
var createProxyFactory = function (fn) {
    var cache = {};
    return function () {
        var args = Array.prototype.join.call(arguments, ',');
        if (args in cache) {
            return cache[args];
        }
        return cache[args] = fn.apply(this, arguments);
    }
}

迭代器模式

相似數組的遍歷...數組

發佈訂閱模式

發佈訂閱模式是一種典型的推模式,即主動向用戶推送數據的方式。通常的函數調用都是拉模式,即用戶主動獲取數據。訂閱模式的一個典型的應用就是rxjs(後面會寫一篇相關的讀書筆記)。書中給出了一個最終版的代碼,但也是存在必定的侷限性,具體實現須要按需解決。緩存

var Event = (function () {
    var global = this, Event, _default = 'default';

    Event = function () {
        var _listen,
            _trigger,
            _remove,
            _slice = Array.prototype.slice,
            _shift = Array.prototype.shift,
            _unshift = Array.prototype.unshift,
            namespaceCache = {},
            _create,
            find,
            // each方法綁定函數做用域爲當前數組項
            each = function (ary, fn) {
                var ret;
                for (var i = 0, l = ary.length; i < l; i++) {
                    var n = ary[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; i >= 0; i--) {
                        if (cache[key][i] === fn) {
                            cache[key].splice(i, 1);
                        }
                    }
                } else {
                    cache[key] = [];
                }
            }
        };

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

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

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

        _create = function (namespace) {
            var namespace = namespace || _default;
            var cache = {},
            offlineStack = [],
            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, _self = this;

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

                    fn = function () {
                        return _trigger.apply(_self, 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;
})();

命令模式

定義一系列的算法,功能不一樣,將他們聚合成一個總體,做爲命令接受者,再定義一箇中間函數,用來根據不一樣的指令調用接受者對象。而實際命令對象只須要來調用這個中間函數而無需直接和接受者交互。命令模式適用於邏輯撤銷邏輯回放的操做。app

組合模式

在父子鏈的函數上面實現相同的接口,在函數調用層面接口保持一致,具體函數邏輯各自獨立。組合模式可讓咱們使用樹形方式建立對象的結構。咱們能夠把相同的操做應用在組合對象和單個對象上。函數

模板方法模式

定義一系列api執行流程,相同部分由父函數實現,不一樣部分api裏面的具體操做交由傳入的函數決定。在js裏面,會更多的去使用高階函數去替代。性能

享元模式

享元模式的核心思想是對象複用,減小對象數量,減小內存開銷。this

職責鏈模式

職責鏈模式的核心思想是,一個函數,分爲兩個部分,一部分:在符合條件的狀況下,處理業務並結束傳遞;另一部分,不符合條件,將業務處理轉交給下一個函數,至於下個函數是誰,經過傳遞參數或者暴露接口來決定,而不是在函數內部寫死。下降函數耦合性。
職責鏈的優勢是下降函數複雜度,缺點是過長的職責鏈可能會形成多段代碼閒置。可能不少中間鏈只起到傳遞參數的做用,下降了性能。spa

中介者模式

一些相互關聯的對象,對象與對象之間隔絕聯繫,而且建立一箇中間對象,將這些對象之間的聯繫與關聯放在中間對象裏面來處理。減小代碼耦合。

裝飾者模式

裝飾者模式與代理模式相似,即在執行目標函數以前或者後進行一些額外的操做,與代理模式的區別在於,裝飾者模式所作的操做不必定與目標函數是一個類型的,所實現的功能也多是千差萬別的。

狀態模式

定義:容許一個對象在其內部狀態改變時改變他它的行爲,對象看起來彷佛修改了它的類。
狀態模式在其內部包含了多種轉態對象,這些狀態對象有着類似的api,在調用這些api的時候,會動態修改狀態模式的當前狀態。從而是狀態類的同一個api作出不一樣的反應。

適配器模式

對目標函數進行數據參數轉化,使其符合目標函數所須要的格式。

設計原則

單一職責原則

一個對象只作一件事情。

最少知識原則

一個軟件實體應當儘量少的與其餘實體發生相互做用。

開放-封閉原則

當須要改變一個程序的功能或者給這個程序增長新功能的時候,可使用增長代碼的方式,可是不容許改動程序的源代碼。
相關文章
相關標籤/搜索