博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
设计模式学习
阅读量:6999 次
发布时间:2019-06-27

本文共 13959 字,大约阅读时间需要 46 分钟。

hot3.png

准备知识:call、apply、闭包、高阶函数。

一、单例模式

        定义:保证一个类只有一个实例,并提供一个访问它的全局访问点。

        1. 用闭包实现单例模式

            优点:将single封装起来,不能手动去修改

            缺点:阅读不舒服,增加程序复杂性,而且构造函数负责了多项职责(创建对象和初始化,保证只有一个对象),违背单一职责原则

        var Single = (function () {

            var single = null;

            var Animal = function (name, age) {

              if (single) {

                    return single;
                }
                this.name = name;
                this.age = age;

                this.init();

               return  single = this;
            }

            Animal .prototype.init = function () {};

            return Animal;

        })();

        var obj = new Single("curry", 29);

        var obj1 = new Single("kaka", 35);

        console.log(obj === obj1); //true

    2、用代理实现单例模式

        优点:将普通类与单例的生成逻辑隔离开,实现了单一职责原则,普通类可以正常创建普通类的对象

        var ProxySingleton = (function () {

            var singleton = {};
            var classNameReg = /^function\s*(\w+)\s*\(/;

            return function () {

                var Class = [].shift.call(arguments);
                var classStr = Class.toString() + Math.random();
                var className = classNameReg.exec(classStr)[1];
                var obj = {};

                var args = arguments;

                function  SingletonClass () {

                    Class.apply(this, args );

                }

                SingletonClass.prototype = Class.prototype;

                if (singleton[className]) {

                    return singleton[className];
                }

                return singleton[className] = new SingletonClass();

            }
        })();

        function Animal (type, name) {

            this.type = type;
            this.name = name;
            this.init();
        }

        Animal.prototype.init = function () {

        };

        var dog = ProxySingleton(Animal, "dog", "dog-01");

        var cat = ProxySingleton(Animal, "cat", "cat-01");
        console.log(dog === cat); //true

3.惰性单例(需要的时候才创建单例,不是在初始化页面时创建单例)

var getSingle = function (fn) {

    var result;

    return function (){

        return result || (result = fn.apply(this, arguments));

    };

};

var createLoginLayer = function () {

    var div = document.createElement("div");

    div.innerHTML = "Login";

    return div;

};

var createSingleLoginLayer = getSingle(createLoginLayer );

var loginLayer = createSingleLoginLayer();

二、工厂模式

    定义:创建一类相似的对象

二、策略模式

    定义:定义一系列的算法,把他们一个个封装起来,并且使他们可以相互替换。

    1、计算奖金例子

    var strategies = {

        S: function (salary) {return return  * 4;},

        A: function (salary) {return return  * 3;},

         B: function (salary) {return return  * 2;}

    };

    var calculateBonus = function (level, salary) {

        return strategies [level](salary);

    };

    calculateBonus ("S", 20000); //80000

    calculateBonus ("A",10000); //30000

三、代理模式(一般代理对象要实现本体对象所有的接口)

    定义:为一个对象提供一个代用品或占位符,以便控制对它的访问。

    1、小明送花例子(通过B将花送给A)

        function Flower () {

        }

        var xiaoming = {

            sendFlower: function (target) {
                target.receiveFlower(new Flower());
            }
        };

        var B = {

            receiveFlower: function (flower) {
                A.listenGoodMood(function () {
                    A.receiveFlower(flower);
                });
            }
        };

        var A = {

            receiveFlower: function (flower) {
                console.log("收到花" + flower);
            },
            listenGoodMood: function (fn) {
                setTimeout(function () {
                    fn();
                }, 5000);
            }
        };

        xiaoming.sendFlower(B);

2.代理实图片预加载

        var myImage = (function () {

            var imgNode = document.createElement("img");

            document.body.appendChild(imgNode);

            return {

                setSrc: function (src) {
                    imgNode.src = src;
                }
            }
        })();

        var proxyImage = (function () {

            var img = document.createElement("img");

            img.onload = function () {

                myImage.setSrc(this.src);
            };

            return {

                setSrc: function (src) {
                    myImage.setSrc("file:// /C:/Users/svenzeng/Desktop/loading.gif");
                    img.src = src;
                }
            }
        })();

        proxyImage.setSrc("http://tupian.enterdesk.com/2013/xll/009/07/7/1.jpg");

3、虚拟代理合并HTTP请求(虚拟代理:把一些开销很大的对象,延迟到真正需要它的时候才去创建)

        function syncFile(id) {

            console.log("开始同步文件,id为:" + id);
        }

        var proxySyncFile = (function () {

            var cache = [], timer;

            return function (id) {

                cache.push(id);

                if (timer) {

                    return;
                }

                timer = setTimeout(function () { //三秒后向本体发送需要同步的ID集合

                    syncFile(cache.join(", "));
                    clearTimeout(timer);
                    timer = null;
                    cache.length = 0;
                }, 3000);
            }
        })();

        var checkbox = document.getElementsByTagName("input");

        for (var i = 0, c = checkbox[i++];) {

            c.onclick = function () {
                if (this.checked) {
                    proxySyncFile(this.id);
                }
            }
        }

    4、缓存代理

        function multi () {

            var value = 1;

            for (var i = 0, l = arguments.length; i < l; i++) {

                value *= arguments[i];
            }

            return value;

        }

        var proxyMulti = (function () {

            var cache = {};

            return function () {

                var args = [].join.call(arguments, ",");

                if (cache[args]) {

                    return cache[args];
                }

                return cache[args] = multi.apply(this, arguments);

            };
        })();

        proxyMulti(1, 2, 3);

        proxyMulti(1, 2, 3);

四、迭代器模式

    定义:提供一种方法顺序访问一个聚合对象中的各个元素,而又不需要暴露该对象的内部表示。

    1、内部迭代器(each函数内部已经定义好迭代规则)

        function each (arr, callback) {

            for (var i = 0, l = arr.length; i < l; i++) {

               i f (callback.call(arr[i], i, arr[i]) === false) {

                    return false;    

                }

            }
        }

        each([1, 2, 3], function (i, n) {

            console.log(i, n);
        });

    2、外部迭代器(必须显示的请求迭代下一个元素,增强了迭代的灵活性,可以手动控制迭代过程或者顺序)

        function Iterater (obj) {

            var current = 0;

            function next () {

                current += 1;
            }

            function isDone () {

                return current >= obj.length;
            }

            function getCurrentItem () {

                return obj[current];
            }

            return {

                next: next,
                isDone: isDone,
                getCurrentItem: getCurrentItem
            };
        }

        function compare (iterater1, iterater2) {

            if  (iterater1.length !==iterater2.length ) {

                throw new Error("iterater1和iterater2不相等");

            }

            

            while (!iterater1.isDone() || !iterater2.isDone()) {
                if (iterater1.getCurrentItem() !== iterater2.getCurrentItem()) {
                    throw new Error("iterater1和iterater2不相等");
                }
                
                iterater1.next();
                iterater2.next();
            }

            console.log("iterater1和iterater2相等");

        }

        var iterater1 = Iterater([1, 2, 3]);

        var iterater2 = Iterater([1, 2, 3]);

        compare(iterater1, iterater2);

五、发布-订阅模式

    定义:又叫观察者模式,定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有倚赖于它的对象都将得到通知。我们一般用事件模型来替代传统的发布-订阅模式。

    1、简单例子    

    var event = {

            clientList: {},
            listen: function (key, callback) {
                if (!this.clientList[key]) {
                    this.clientList[key] = [];
                }

                this.clientList[key].push(callback)

            },
            remove: function (key, callback) {
                var callbacks = this.clientList[key];

                if (!callbacks) {

                    return false;
                }

                if (!callback) { //取消key对应消息的所有订阅

                    callbacks.length = 0;
                } else {
                    for (var i = callbacks.length - 1; i >= 0; i--) {
                        if (callbacks[i] === callback) {
                            callbacks.splice(i, 1);
                        }
                    }
                }
            },
            trigger: function () {
                var key = Array.prototype.shift.call(arguments);
                var callbacks = this.clientList[key];
                
                if (!callbacks || callbacks.length === 0) {
                    return false;
                }

                for (var i = 0, len = callbacks.length; i < len; i++) {

                    callbacks[i].apply(this, arguments);
                }
            }
        };

        function installEvent (obj) {

            for (var i in event) {
                obj[i] = event[i];
            }
        }

        var salesOffices = {};

        installEvent(salesOffices);

        salesOffices.listen("squareMeter88", function (price) {

            console.log("价格= " + price);
        });

        salesOffices.listen("squareMeter100", function (price) {

            console.log("价格= " + price);
        });

        salesOffices.trigger("squareMeter88", 1000000);

        salesOffices.trigger("squareMeter100", 1300000);

    2、Event作为中介者的角色,把订阅者和发布者联系起来

        var Event = {

            clientList: {},
            listen: function (key, callback) {
                if (!this.clientList[key]) {
                    this.clientList[key] = [];
                }

                this.clientList[key].push(callback)

            },
            remove: function (key, callback) {
                var callbacks = this.clientList[key];

                if (!callbacks) {

                    return false;
                }

                if (!callback) { //取消key对应消息的所有订阅

                    callbacks.length = 0;
                } else {
                    for (var i = callbacks.length - 1; i >= 0; i--) {
                        if (callbacks[i] === callback) {
                            callbacks.splice(i, 1);
                        }
                    }
                }
            },
            trigger: function () {
                var key = Array.prototype.shift.call(arguments);
                var callbacks = this.clientList[key];
                
                if (!callbacks || callbacks.length === 0) {
                    return false;
                }

                for (var i = 0, len = callbacks.length; i < len; i++) {

                    callbacks[i].apply(this, arguments);
                }
            }
        };

        Event.listen("squareMeter88", function (price) {

            console.log("价格= " + price);
        });

        Event.listen("squareMeter100", function (price) {

            console.log("价格= " + price);
        });

        Event.trigger("squareMeter88", 1000000);

        Event.trigger("squareMeter100", 1300000);

    3、加入缓存和命名空间

        var Event = (function () {

            var _default = "default",
                _listen,
                _trigger,
                _remove,
                _slice = Array.prototype.slice,
                _shift = Array.prototype.shift,
                _unshift = Array.prototype.unshift,
                namespaceCache = {},
                _create,
                each = function (ary, fn) {
                    var ret;

                    for (var i = 0, l = ary.length; i < l; i++) {

                        ret = fn.call(ary[i], i, ary[i]);
                    }

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

                        }
                    };

                console.log(namespaceCache);

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

        /******** 先发布后订阅 ************/

        Event.trigger("click", 1);
        Event.listen("click", function (a) {
            console.log(a);
        });

        /******** 使用命名空间 ************/

        Event.create("namespace1").listen("click", function (a) {
            console.log(a);
        });

        Event.create("namespace1").trigger("click", 2);

        Event.create("namespace1").listen("click", function (a) {

            console.log(a);
        });

六、命令模式(解耦命令请求者与命令接受者之间的强耦合关系,当我们不知道命令接受者是谁,以及被请求的操作是什么时,适合用命令模式)

    1、传统命令模式例子

        <!DOCTYPE html>

        <html lang="en">
        <head>
            <meta charset="UTF-8">
            <title>Document</title>
        </head>
        <body>
            <button id="refresh">refresh</button>
            <button id="add">add</button>
            <button id="del">del</button>
            <script type="text/javascript">
                var refreshBtn = document.getElementById("refresh");
                var addBtn = document.getElementById("add");
                var delBtn = document.getElementById("del");

                function setCommand (button, command) {

                    button.addEventListener("click", function () {
                        command.excute();
                    }, false);
                }

                var MenuBar = {

                    refresh: function () {
                        console.log("刷新菜单目录");
                    }
                };

                var SubMenu = {

                    add: function () {
                        console.log("增加子菜单");
                    },
                    del: function () {
                        console.log("删除子菜单");
                    }
                };

                function RefershMenuBarCommand (receiver) {

                    this.receiver = receiver;
                }

                RefershMenuBarCommand.prototype.excute = function () {

                    this.receiver.refresh();
                };

                function AddSubMenuCommand (receiver) {

                    this.receiver = receiver;
                }

                AddSubMenuCommand.prototype.excute = function () {

                    this.receiver.add();
                };

                function DelSubMenuCommand (receiver) {
                    this.receiver = receiver;
                }

                DelSubMenuCommand.prototype.excute = function () {

                    this.receiver.del();
                };

                var refershMenuBarCommand = new RefershMenuBarCommand(MenuBar);

                var addSubMenuCommand = new AddSubMenuCommand(SubMenu);
                var delSubMenuCommand = new DelSubMenuCommand(SubMenu);

                setCommand(refreshBtn, refershMenuBarCommand);

                setCommand(addBtn, addSubMenuCommand);
                setCommand(delBtn, delSubMenuCommand);
            </script>
        </body>
        </html>

    2. js命令模式(闭包实现)

        <!DOCTYPE html>

        <html lang="en">
        <head>
            <meta charset="UTF-8">
            <title>Document</title>
        </head>
        <body>
            <button id="refresh">refresh</button>
            <button id="add">add</button>
            <button id="del">del</button>
            <script type="text/javascript">
                function setCommand (btn, command) {
                    btn.onclick = function () {
                        command.excute();
                    };
                };
                var Menubar = {
                    refresh: function () {
                        console.log("刷新菜单界面");
                    }
                };
                function RefreshMenubarCommand (receiver) {
                    return {
                        excute: function () {
                            receiver.refresh();
                        }
                    };
                };

                var refreshMenubarCommand = RefreshMenubarCommand(Menubar);

                setCommand(document.getElementById("refresh"), refreshMenubarCommand);
            </script>
        </body>
        </html>

    3. 执行和撤销

        <!DOCTYPE html>

        <html lang="en">
        <head>
            <meta charset="UTF-8">
            <title>Document</title>
            <style type="text/css">
                #ball {
                    position: absolute;
                    top: 100px;
                    left: 0;
                    width: 100px;
                    height: 100px;
                    border-radius: 50%;
                    background: #eee;
                }
            </style>
        </head>
        <body>
            <input type="text" id="pos">
            <button id="excute">excute</button>
            <button id="uodo">uodo</button>
            <select id="direction">
                <option value="top">top</option>
                <option value="bottom">bottom</option>
                <option value="left" selected>left</option>
                <option value="right">right</option>
            </select>
            <div id="ball"></div>
            <script type="text/javascript">
                var ball = document.getElementById("ball");
                var direction = document.getElementById("direction");
                var pos = document.getElementById("pos");
                var excuteBtn = document.getElementById("excute");
                var uodoBtn = document.getElementById("uodo");
                var moveCommandStack = [];
                var ballAction = {
                    dom: ball,
                    move: function (direction, pos) {
                        this.dom.style[direction] = pos + "px";
                    }
                };

                function MoveCommand(recevier, attr, pos) {

                    this.recevier = recevier;
                    this.pos = pos;
                    this.attr = attr;
                    this.oldPos = this.recevier.dom.getBoundingClientRect()[attr];
                }

                MoveCommand.prototype.excute = function () {

                    this.recevier.move(this.attr, this.pos);
                };
                MoveCommand.prototype.uodo = function () {
                    this.recevier.move(this.attr, this.oldPos);
                };

                excuteBtn.onclick = function () {

                    var value = pos.value;
                    var directionStr = direction.selectedOptions[0].value;
                    var command;

                    if (!value || !direction) {

                        return false;
                    }

                    command = new MoveCommand(ballAction, directionStr, pos.value);

                    command.excute();
                    moveCommandStack.push(command);
                };
                uodoBtn.onclick = function () {
                    var command = moveCommandStack.pop();

                    if (!command) {

                        return false;
                    }

                    command.uodo();

                };
            </script>
        </body>
        </html>

二、原型模式(克隆)

    Object.create = Object.create || function (obj) {

        function Clone () {}

        Clone.prototype = obj;

        return new Clone();

    }

 

转载于:https://my.oschina.net/u/1992917/blog/1162912

你可能感兴趣的文章
javascript实现朴素贝叶斯分类与决策树ID3分类
查看>>
理解与使用Javascript中的回调函数
查看>>
android ijkplayer c层分析-初始化
查看>>
vue2 + vuex 高度还原 饿了么 App,与官方后台真实数据交互,获取商品信息,实现登陆、购物车、下单等功能...
查看>>
JS可以写操作系统?Windows 95被装进Electron App
查看>>
Studio 3T:MongoDB SQL探究
查看>>
为什么中台是传统企业数字化转型的关键?
查看>>
中国技术开放日专场在美国旧金山隆重开幕
查看>>
从责任界定和问题预警角度 解读全栈溯源对DevOps的价值
查看>>
自动化运维经验谈,以及为什么Docker是革命性的
查看>>
百度发布开源智能边缘计算平台OpenEdge
查看>>
JavaScript引擎V8 5.1遵循了更多的ECMAScript规范并支持WASM
查看>>
如何用“区块链+稳定币”技术来提升资产证券化市场运作效率
查看>>
Spring Web Services 3.0.4.RELEASE和2.4.3.RELEASE发布
查看>>
广度、深度、易用性,详解6大机器学习云
查看>>
雇佣和留住开发人员,打造优秀的团队
查看>>
马斯克宣布退出OpenAI,全身心投入特斯拉与SpaceX
查看>>
LFE将Lisp编程带到Erlang虚拟机上
查看>>
VS2017 15.8第二个预览版本提升了对CPU Profiling和F#的支持
查看>>
荷兰商业银行使用精益领导力推行改进
查看>>