2

设计模式

单例模式

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);

代理模式

虚拟代理

虚拟代理在不改变原有函数(对象)的方法结构的前提下,定义新的对象,并且实现同样的接口,给被代理函数赋予额外的行为与逻辑,做一些过滤、合并、预处理等操作。

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;
})();

命令模式

定义一系列的算法,功能不同,将他们聚合成一个整体,作为命令接受者,再定义一个中间函数,用来根据不同的指令调用接受者对象。而实际命令对象只需要来调用这个中间函数而无需直接和接受者交互。命令模式适用于逻辑撤销逻辑回放的操作。

组合模式

在父子链的函数上面实现相同的接口,在函数调用层面接口保持一致,具体函数逻辑各自独立。组合模式可以让我们使用树形方式创建对象的结构。我们可以把相同的操作应用在组合对象和单个对象上。

模板方法模式

定义一系列api执行流程,相同部分由父函数实现,不同部分api里面的具体操作交由传入的函数决定。在js里面,会更多的去使用高阶函数去替代。

享元模式

享元模式的核心思想是对象复用,减少对象数量,减少内存开销。

职责链模式

职责链模式的核心思想是,一个函数,分为两个部分,一部分:在符合条件的情况下,处理业务并结束传递;另外一部分,不符合条件,将业务处理转交给下一个函数,至于下个函数是谁,通过传递参数或者暴露接口来决定,而不是在函数内部写死。降低函数耦合性。
职责链的优点是降低函数复杂度,缺点是过长的职责链可能会造成多段代码闲置。可能很多中间链只起到传递参数的作用,降低了性能。

中介者模式

一些相互关联的对象,对象与对象之间隔绝联系,并且创建一个中间对象,将这些对象之间的联系与关联放在中间对象里面来处理。减少代码耦合。

装饰者模式

装饰者模式与代理模式类似,即在执行目标函数之前或者后进行一些额外的操作,与代理模式的区别在于,装饰者模式所做的操作不一定与目标函数是一个类型的,所实现的功能也可能是千差万别的。

状态模式

定义:允许一个对象在其内部状态改变时改变他它的行为,对象看起来似乎修改了它的类。
状态模式在其内部包含了多种转态对象,这些状态对象有着相似的api,在调用这些api的时候,会动态修改状态模式的当前状态。从而是状态类的同一个api做出不同的反应。

适配器模式

对目标函数进行数据参数转化,使其符合目标函数所需要的格式。

设计原则

单一职责原则

一个对象只做一件事情。

最少知识原则

一个软件实体应当尽可能少的与其他实体发生相互作用。

开放-封闭原则

当需要改变一个程序的功能或者给这个程序增加新功能的时候,可以使用增加代码的方式,但是不允许改动程序的源代码。

吴霸霸
300 声望14 粉丝