文档章节

javascript设计模式

L
 LorinLuo
发布于 2017/08/09 10:16
字数 5860
阅读 32
收藏 1

设计模式

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

1. call、apply:改变this指向

2. 闭包:函数作用域嵌套,内层作用域可以访问外层作用域

利用闭包模拟bind:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<script type="text/javascript">
	Function.prototype.bind = function () {
		var self = this;
		var context = [].shift.call(arguments);
		var args = [].slice.call(arguments);

		return function () {
			return self.apply(context, [].concat.apply(args, [].slice.call(arguments)));
		};
	};

	function say (initValue1, initValue2, word) {
		console.log(this);
		console.log(initValue1);
		console.log(initValue2);
		console.log(word);
	}

	say.bind({name: 1}, "initValue1", "initValue2")("bind");
</script>
</body>
</html>

闭包形成过程:

闭包形成过程

3. 高阶函数:函数作为参数传递或者函数作为返回值返回(如回调函数和闭包返回的函数)

利用闭包和高阶函数重写类型判断

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>Document</title>
</head>
<body>
	<script type="text/javascript">
		var toString = Object.prototype.toString;
		var Type = {};

		function isNumber(value) {
			return toString.call(value) === "[object Number]";
		}

		function isString(value) {
			return toString.call(value) === "[object String]";
		}

		function isArray(value) {
			return toString.call(value) === "[object Array]";
		}

		function isFunction(value) {
			return toString.call(value) === "[object Function]";
		}

		function isTypeFactory(type) {
			return function (value) {
				return Object.prototype.toString.call(value).toUpperCase() === ("[object " + type + "]").toUpperCase();
			};
		}

		for (var i = 0, types = ['Number', 'String', 'Array', 'Function'], type; type = types[i++];) {
			Type['is' + type] = isTypeFactory(type);
		}
	</script>
</body>
</html>

二、工厂模式

定义、提供一个通用的接口来批量生产一类相似对象,我们可以指定我们所希望的工厂对象的类型

1. 简单工厂

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<script type="text/javascript">
	function Button(name) {
		this.dom = document.createElement("button");
		this.name = name;
	}

	function Input(name) {
		this.dom = document.createElement("input");
		this.name = name;
	}

	function UIFactory() {
		
	}

	UIFactory.prototype.createUI = function () {
		var type = [].shift.call(arguments);
		var UIClass = null, ui;

		function UI() {

		}

		switch(type) {
			case "Button":
				UIClass = Button;
				break;
			case "Input":
				UIClass = Input;
				break;
			default : null;
		}

		if (!UIClass) {
			return {};
		}
		
		UI.prototype = UIClass.prototype;
		ui = new UI;
		UIClass.apply(ui, arguments);
		return obj;
	}

	var uiFactory = new UIFactory();
	var button = uiFactory.createUI("Button", "button");
	var input = uiFactory.createUI("Input", "input");
</script>
</body>
</html>

2. 抽象工厂(一组具有共同目的的单个工厂)

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</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">
	function Animal () {

	}

	function People (type, name, age) {
		this.type = type;
		this.name = name;
		this.age = age;
	}

	People.prototype = new Animal();
	People.prototype.constructor = People;
	People.prototype.run = function () {
		console.log(this.name + "run");
	};

	function PeopleFactory (type, name, age) {
		var people = new People(type, name, age);

		people.height = "175cm";
		return people;
	}

	function Bird (type, color) {
		this.type = type;
		this.color = color;
	}

	Bird.prototype = new Animal();
	Bird.prototype.constructor = Bird;
	Bird.prototype.fly = function () {
		console.log(this.type + "fly");
	};

	function BirdFactory (type, color) {
		return new Bird(type, color);
	}

	var AbstractAnimalFactory = (function () {
		var types = {};

		function registerFactory (type, AnimalFactory) {
			if (Animal.prototype.constructor !== Animal) {
				return AbstractAnimalFactory;
			}

			types[type] = AnimalFactory;
			return AbstractAnimalFactory;
		}

		function createAnimal () {
			var animalType = [].shift.call(arguments);
			var AnimalType = types[animalType];
			var animal = AnimalType.apply({}, arguments);

			return animal || null;
		}

		return {
			registerFactory: registerFactory,
			createAnimal: createAnimal
		};
	})();

	AbstractAnimalFactory.registerFactory("people", PeopleFactory);
	AbstractAnimalFactory.registerFactory("bird", BirdFactory);

	var kaka = AbstractAnimalFactory.createAnimal("people", "male", "kaka", "32");
	var bird1 = AbstractAnimalFactory.createAnimal("bird", "xiao", "red");
</script>
</body>
</html>

三、单例模式

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

1. 普通单例模式

缺点:阅读不舒服,增加程序复杂性,而且构造函数负责了多项职责(创建对象和初始化,保证只有一个对象),违背单一职责原则
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<script type="text/javascript">
	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
</script>
</body>
</html>

2. 利用代理实现单例模式(需要的时候才创建单例,而不是初始化时创建单例)

优点:将普通类与单例的生成逻辑隔离开,实现了单一职责原则,普通类可以正常创建普通类的对象
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<script type="text/javascript">
    var ProxySingleton = (function () {
        var singleton = {};
        var classNameReg = /^function\s*(\w+)\s*\(/;

        return function () {
            var Class = [].shift.call(arguments);
            var classStr = Class.toString();
            var className = classNameReg.exec(classStr)[1];
            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;
    }

    var dog = ProxySingleton(Animal, "dog", "dog-01");
    var cat = ProxySingleton(Animal, "cat", "cat-01");
    console.log(dog === cat); //true
</script>
</body>
</html>

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

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<script type="text/javascript">
	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();
</script>
</body>
</html>

四、策略模式

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

1. 计算奖金例子

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<script type="text/javascript">
    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
</script>
</body>
</html>

2. 策略模式实现表单验证

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<script type="text/javascript">
	const strategies = {};

	strategies.notEmpty = (value, errMsg, validateField) => {
	  if (!value) {
	    return {
	      errMsg: errMsg,
	      validateField: validateField
	    };
	  }
	};
	strategies.isNumber = (value, errMsg, validateField) => {
	  try {
	    if (!String.prototype.match.call(value, /^\d+$/)) {
	      return {
	        errMsg: errMsg,
	        validateField: validateField
	      };
	    }
	  } catch(err) {
	    return;
	  }
	};

	function Validator() {
	    this.cache = [];
	    this.validateRes = null;
	    this.timer = null;
	}

	Validator.prototype.add = function (value, rule, errMsg, validateField) {
		this.cache.push(function () {
			return strategies[rule].apply(strategies, [value, errMsg, validateField]);
		});
	};
	Validator.prototype.validate = function (isAll) {
	    let cache = this.cache;
	    let validateResArr = [];

	    this.validateRes = null;

	    if (!isAll) {
	      for (let i = 0, validateFucn; validateFucn = cache[i++];) {
	        let validateRes = validateFucn();

	        if (validateRes) {
	          return this.validateRes = validateRes;
	        }
	      }

	      return null;
	    }

	    cache.forEach(function (validateFucn) {
	      let validateRes = validateFucn();

	      if (validateRes) {
	        validateResArr.push(validateRes);
	      }
	    });

	    return this.validateRes = validateResArr;
	};

	var validator = new Validator();

	validator.add("", "notEmpty", "名字必须非空", "name");
	validator.add("fefr", "isNumber", "手机必须由数字组成", "phone");
</script>
</body>
</html>

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

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

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

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<script type="text/javascript">
    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);
</script>
</body>
</html>

2.虚拟代理实图片预加载(虚拟代理将一些开销很大的对象,延迟到真正需要它的时候才起创建)

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<script type="text/javascript">
    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 () {
        	var self = this;
            setTimeout(function () {
            	myImage.setSrc(self.src);
            }, 1500);
        };
        return {
            setSrc: function (src) {
                myImage.setSrc("https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1502270518122&di=b3dcb328920e8feef3c1fbb4ed00ea9c&imgtype=0&src=http%3A%2F%2Fimg.zcool.cn%2Fcommunity%2F01a0d456052ebc32f875a132c28f69.gif");
                img.src = src;
            }
        }
    })();

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

3、虚拟代理合并HTTP请求

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<input type="checkbox" id="1">
<input type="checkbox" id="2">
<input type="checkbox" id="3">
<input type="checkbox" id="4">
<input type="checkbox" id="5">
<script type="text/javascript">
    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);
            }
        }
    }
</script>
</body>
</html>

3、缓存代理

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<script type="text/javascript">
    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);
</script>
</body>
</html>

六、迭代器模式

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

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

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<script type="text/javascript">
    function each (arr, callback) {
        for (var i = 0, l = arr.length; i < l; i++) {
           if (callback.call(arr[i], i, arr[i]) === false) {
                return false;    
            }
        }
    }

    each([1, 2, 3], function (i, n) {
        console.log(i, n);
    });
</script>
</body>
</html>

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

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<script type="text/javascript">
    function Iterater (obj) {
        var current = 0;
        function next () {
            current += 1;
        }
        function isDone () {
            return current >= obj.length;
        }
        function getCurrentItem () {
            return obj[current];
        }
        function getLength() {
           return obj.length;
        }

        return {
            next: next,
            isDone: isDone,
            getCurrentItem: getCurrentItem,
            getLength: getLength
        };
    }

    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);
</script>
</body>
</html>

七、发布-订阅模式(观察者模式)

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

1、简单例子

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<script type="text/javascript">
	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);
</script>
</body>
</html>

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

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<script type="text/javascript">
    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);
</script>
</body>
</html>

八、命令模式(解耦命令请求者与命令接受者之间的强耦合关系,当我们不知道命令接受者是谁,以及被请求的操作是什么时,适合用命令模式;命令模式中的命令指的是一个执行某些特定事情的指令)

1、闭包实现模式

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<button id="refresh">refresh</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>

2. 命令模式实现撤销功能

<!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>

九、中介者模式(解除对象与对象之间的紧耦合关系,所有的相关对象都通过中介者对象来通信,将多多的的网状关系解耦成一对多关系)

1、利用发布-订阅模式实现中介者模式,将中介者实现为订阅者,通信对象实现为发布者,当通信对象状态发生改变,便推送消息给中介者,中介者处理消息并通知其他通信对象。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>
    <script type="text/javascript">
        var moduleA = (function () {
            return {
                sendMsg: function (data) {
                    Event.trigger("msgAToB", data);
                },
                receiveMsg: function (data) {
                    console.log(data);
                }
            }
        })();

        var moduleB = (function () {
            return {
                sendMsg: function (data) {
                    Event.trigger("msgBToA", data);
                },
                receiveMsg: function (data) {
                    console.log(data);
                }
            }
        })();

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

                this.clientList[key].push(callback);
            },
            trigger: function () {
                var key = Array.prototype.shift.call(arguments),
                    fns = this.clientList[key];

                if (!fns || fns.listen === 0) {
                    return false;
                }

                for (var i = 0, fn; fn = fns[i++];) {
                    fn.apply(this, arguments);
                }
            },
            clientList: {}
        };

        Event.listen("msgAToB", function (data) {
            moduleB.receiveMsg(data);
        });

        Event.listen("msgBToA", function (data) {
            moduleA.receiveMsg(data);
        });

        moduleA.sendMsg("A TO B");
        moduleB.sendMsg("B TO A");
    </script>
</body>
</html>

2、利用普通对象实现中介者模式,只需要中介者开放一些接受通信者消息的接口。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>
    <script type="text/javascript">
        var moduleA = (function () {
            return {
                sendMsg: function (receiver, data) {
                    mediator.sendMsg(receiver, data);
                },
                receiveMsg: function (data) {
                    console.log(data);
                }
            }
        })();

        var moduleB = (function () {
            return {
                sendMsg: function (receiver, data) {
                    mediator.sendMsg(receiver, data);
                },
                receiveMsg: function (data) {
                    console.log(data);
                }
            }
        })();

        var mediator = {
            sendMsg: function (receiver, data) {
                receiver.receiveMsg(data);
            }
        };

        moduleA.sendMsg(moduleB, "A TO B");
        moduleB.sendMsg(moduleA, "B TO A");
    </script>
</body>
</html>

十、适配器模式(解决两个软件实体间的接口不兼容的问题,使用适配器模式之后,原本由于接口不兼容而不能工作的两个软件实体可以一起工作,适配器模式更多的是一种补救措施)

1、实例1

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>
    <script type="text/javascript">
        var googleMap = {
            show: function() {
                console.log("开始渲染谷歌地图");
            }
        };

        var baiduMap = {
            show: function() {
                console.log("开始渲染百度地图");
            }
        };

        var gaodeMap = {
            display: function() {
                console.log("开始渲染高德地图");
            }
        };

        //高德地图为后加入的地图,由于渲染地图的接口名称于其他地图不一致,因此需要增加适配器进行转换
        var gaodeMapAdapter = {
            show: function () {
                return gaodeMap.display();
            }
        };

        var renderMap = function(map) {
            if (map.show instanceof Function) {
                map.show();
            }
        };

        renderMap(googleMap);
        renderMap(baiduMap);
        renderMap(gaodeMapAdapter);
    </script>
</body>
</html>

十一、装饰者模式(给对象动态的增加职责)

1、简单例子

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>
    <script type="text/javascript">
        var plan = {
            fire: function() {
                console.log("发送普通子弹");
            }
        };

        var missileDecorator = function() {
            console.log("发送导弹");
        };

        var atomDecorator = function() {
            console.log("发送原子弹");
        };

        function decoratorPlan(origin, decorator) {
            var originFire = origin.fire;

            origin.fire = function () {
                originFire();
                decorator();
            };
        }

        decoratorPlan(plan, missileDecorator);
        decoratorPlan(plan, atomDecorator);
        plan.fire();
    </script>
</body>
</html>

2、用装饰者模式扩展window.onload事件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>
    <script type="text/javascript">
        function addEventLoad(callback) {
            var oldCallback = window.onload;

            window.onload = function () {
                if (oldCallback) {
                    oldCallback();
                }

                callback();
            };
        }

        addEventLoad(function () {
            console.log(1);
        });

        addEventLoad(function () {
            console.log(2);
        });
    </script>
</body>
</html>

3、利用AOP实现装饰者模式

AOP:面向切片编程(主要作用是把一些跟核心业务逻辑无关的功能代码抽离出来)
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>
    <script type="text/javascript">
        Function.prototype.before = function(beforeFn) {
            var self = this;

            return function() {
                beforeFn.apply(this, arguments);
                return self.apply(this, arguments);
            };
        };

        Function.prototype.after = function(afterFn) {
            var self = this;

            return function() {
                var returnValue = self.apply(this, arguments);
                afterFn.apply(this, arguments);
                return returnValue;
            };
        };

        function addEventLoad(fn) {
            window.onload = (window.onload || function() {}).after(fn);
        };

        addEventLoad(function () {
            console.log(1);
        });

        addEventLoad(function () {
            console.log(2);
        });
    </script>
</body>
</html>

十二、组合模式(用小的子对象来构建更大的对象)

1、宏命令与组合模式

宏命令:一组命令的集合,通过执行宏命令的方式可以一次执行一批命令
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>
    <script type="text/javascript">
        var MacroCommand = function() {
            return {
                commandList: [],
                add: function(command) {
                    this.commandList.push(command);
                },
                excute: function() {
                    for (var i = 0, command; command = this.commandList[i++];) {
                        command.excute();
                    }
                }
            };
        };

        var openAcCommand = {
            excute: function() {
                console.log("打开空调");
            },
            add: function() {
                throw new Error("叶子对象不能添加子节点");
            }
        };

        var openTvCommand = {
            excute: function() {
                console.log("打开电视");
            },
            add: function() {
                throw new Error("叶子对象不能添加子节点");
            }
        };

        var openSoundCommand = {
            excute: function() {
                console.log("打开音响");
            },
            add: function() {
                throw new Error("叶子对象不能添加子节点");
            }
        };

        var macroCommand1 = MacroCommand();
        macroCommand1.add(openTvCommand);
        macroCommand1.add(openSoundCommand);

        var closeDoorCommand = {
            excute: function() {
                console.log("关门");
            },
            add: function() {
                throw new Error("叶子对象不能添加子节点");
            }
        };

        var openPcCommand = {
            excute: function() {
                console.log("打开电脑");
            },
            add: function() {
                throw new Error("叶子对象不能添加子节点");
            }
        };

        var openQQCommand = {
            excute: function() {
                console.log("登录QQ");
            },
            add: function() {
                throw new Error("叶子对象不能添加子节点");
            }
        };

        var macroCommand2 = MacroCommand();
        macroCommand1.add(closeDoorCommand);
        macroCommand1.add(openPcCommand);
        macroCommand1.add(openQQCommand);

        var macroCommand = MacroCommand();
        macroCommand.add(openAcCommand);
        macroCommand.add(macroCommand1);
        macroCommand.add(macroCommand2);

        macroCommand.excute();
    </script>
</body>
</html>

十三、模板方法模式(是一种只需使用继承就可以实现的非常简单的模式,由两部分构成,抽象父类和具体的实现子类,父类封装了子类的算法框架和执行顺序,是为了解决相同的行为在各个子类中重复实现的问题)

1、利用原型继承咖啡与茶的例子

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>
    <script type="text/javascript">
        function Beverage() {}

        Beverage.prototype.boilWater = function() {
            console.log("把水煮沸");
        };

        Beverage.prototype.brew = function() {} //空方法,应该由子类重写
        Beverage.prototype.pourInCup = function() {} //空方法,应该由子类重写
        Beverage.prototype.addCondiments = function() {} //空方法,应该由子类重写
        Beverage.prototype.init = function() { //模板方法,该方法封装了子类的算法框架,它作为一个算法的模板,指导子类以和政顺序去执行哪些方法
            this.boilWater();
            this.brew();
            this.pourInCup();
            if  (this.customerWantsCondiments()) {
                this.addCondiments();
            }
        };

        function Coffee() {}

        Coffee.prototype = new Beverage();
        Coffee.prototype.brew = function() {
            console.log("用沸水冲泡咖啡");
        };
        Coffee.prototype.pourInCup = function() {
            console.log("把咖啡倒进杯子");
        };
        Coffee.prototype.addCondiments = function() {
            console.log("加糖和牛奶");
        };

        function Tea() {}

        Tea.prototype = new Beverage();
        Tea.prototype.brew = function() {
            console.log("用沸水浸泡茶叶");
        };
        Tea.prototype.pourInCup = function() {
            console.log("把茶叶倒进杯子");
        };
        Tea.prototype.addCondiments = function() {
            console.log("加柠檬");
        };
        Tea.prototype.customerWantsCondiments = function(){ //钩子方法(hook)
            return true;
        };

        var coffee = new Coffee();
        var tea = new Tea();

        coffee.init();
        tea.init();
    </script>
</body>
</html>

2、利用高阶函数咖啡与茶的例子

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>
    <script type="text/javascript">
        function Beverage(param) {
            var boilWater = function() {
                console.log("把水煮沸");
            };

            var brew = param.brew || function() {
                throw new Error("必须传递brew方法");
            }

            var pourInCup = param.pourInCup || function() {
                throw new Error("必须传递pourInCup方法");
            }


            var addCondiments = param.addCondiments || function() {
                throw new Error("必须传递addCondiments方法");
            }

            var F = function() {};
            F.prototype.init = function() { //模板方法
                boilWater();
                brew();
                pourInCup();
                addCondiments();
            };
            return F;
        }

        var Coffee = Beverage({
            brew: function() {
                console.log("用沸水冲泡咖啡");
            },
            pourInCup: function() {
                console.log("把咖啡倒进杯子");
            },
            addCondiments: function() {
                console.log("加糖和牛奶");
            }
        });

        var Tea = Beverage({
            brew: function() {
                console.log("用沸水浸泡茶叶");
            },
            pourInCup: function() {
                console.log("把茶叶倒进杯子");
            },
            addCondiments: function() {
                console.log("加柠檬");
            }
        });

        var coffee = new Coffee();
        var tea = new Tea();

        coffee.init();
        tea.init();
    </script>
</body>
</html>

十四、享元模式(是一种用于性能优化的模式,享元模式的核心是运用共享技术来有效支持大量细粒度的对象,如果系统中因为创建了大量类似的对象导致内存占用过高,享元模式就非常有用了,享元模式要求将对象的属性划分为内部状态和外部状态,内部状态一般是不会改变的可以共享的状态,外部状态是改变的状态)

1、文件上传例子

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>
    <script type="text/javascript">
        function Upload(uploadType) {
            this.uploadType = uploadType;
        }

        Upload.prototype.delFile = function(id) {
            uploadManager.setExternaLState(id, this);

            if (this.fileSize < 3000 || window.confirm("确定要删除改文件吗?" + this.fileName)) {
                return this.dom.parentNode.removeChild(this.dom);
            }
        };

        var UploadFactory = (function() {
            var cache = {};

            return {
                create: function(uploadType) {
                    return cache[uploadType] || (cache[uploadType] = new Upload(uploadType));
                }
            };
        })();

        var uploadManager = (function() {
            var uploadDatabase = {};

            return {
                add: function(id, uploadType, fileName, fileSize) {
                    var flyWeightObj = UploadFactory.create(uploadType);
                    var dom = document.createElement("div");
                    dom.innerHTML = '<span>文件名称:' + fileName + '文件大小:' + fileSize + '</span>' + '<button class="delFile">删除</button>'
                    dom.querySelector('.delFile').onclick = function() {
                        flyWeightObj.delFile(id);
                    };
                    document.body.appendChild(dom);
                    uploadDatabase[id] = {
                        fileName: fileName,
                        fileSize: fileSize,
                        dom: dom
                    };

                    return flyWeightObj;
                },
                setExternaLState: function(id, flyWeightObj) {
                    var uploadData = uploadDatabase[id];

                    for (var i in uploadData) {
                        flyWeightObj[i] = uploadData[i];
                    }
                }
            };
        })();

        var id = 0;

        window.startUpload = function(uploadType, files) {
            for (var i = 0, file; file = files[i++];) {
                var uploadObj = uploadManager.add(++id, uploadType, file.fileName, file.fileSize);
            }
        }

        startUpload("plugin", [
            {fileName: '1.txt', fileSize: 1000},
            {fileName: '2.txt', fileSize: 2000}
        ]);


        startUpload("flash", [
            {fileName: '3.txt', fileSize: 3000},
            {fileName: '4.txt', fileSize: 4000}
        ]);
    </script>
</body>
</html>

十五、职责链模式(使多个对象都有机会处理请求,从而避免请求的发送者和接受者之间的耦合关系,将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止)

1、例子一

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>
    <script type="text/javascript">
        function order500(orderType, pay, stock) {
            if (orderType === 1 && pay) {
                console.log("500元定金预购,得到100元优惠券");
            } else {
                return "nextSuccessor"; //我不知道下一个节点是谁,反正把请求往后面传递
            }
        }

        function order200(orderType, pay, stock) {
            if (orderType === 2 && pay) {
                console.log("200元定金预购,得到50元优惠券");
            } else {
                return "nextSuccessor";
            }
        }

        function orderNormal(orderType, pay, stock) {
            if (stock > 0) {
                console.log("普通购买,无优惠");
            } else {
                console.log("手机库存不足");
            }
        }

        function Chain(fn) {
            this.fn = fn;
            this.successor = null;
        }

        Chain.prototype.setNextSuccessor = function(successor) {
            this.successor = successor;
        };

        Chain.prototype.passRequest = function() {
            var ret = this.fn.apply(this, arguments);

            if (ret === "nextSuccessor") {
                return this.successor && this.successor.passRequest.apply(this.successor, arguments);
            }   

            return ret;
        };

        var chainOrder500 = new Chain(order500);
        var chainOrder200 = new Chain(order200);
        var chainOrderNormal = new Chain(orderNormal);

        chainOrder500.setNextSuccessor(chainOrder200);
        chainOrder200.setNextSuccessor(chainOrderNormal);

        chainOrder500.passRequest(1, true, 500);
        chainOrder500.passRequest(2, true, 500);
        chainOrder500.passRequest(3, true, 500);
        chainOrder500.passRequest(1, false, 0);
    </script>
</body>
</html>

2、用AOP实现职责链(缺点:叠加了函数的作用域,多层闭包嵌套,影响性能)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>
    <script type="text/javascript">
        function order500(orderType, pay, stock) {
            if (orderType === 1 && pay) {
                console.log("500元定金预购,得到100元优惠券");
            } else {
                return "nextSuccessor"; //我不知道下一个节点是谁,反正把请求往后面传递
            }
        }

        function order200(orderType, pay, stock) {
            if (orderType === 2 && pay) {
                console.log("200元定金预购,得到50元优惠券");
            } else {
                return "nextSuccessor";
            }
        }

        function orderNormal(orderType, pay, stock) {
            if (stock > 0) {
                console.log("普通购买,无优惠");
            } else {
                console.log("手机库存不足");
            }
        }

        Function.prototype.after = function(fn) {
            var self = this;

            return function() {
                var ret = self.apply(this, arguments);

                if (ret === "nextSuccessor") {
                    return fn.apply(this, arguments);
                }

                return ret;
            };
        };

        var order = order500.after(order200).after(orderNormal);

        order(1, true, 500);
        order(2, true, 500);
        order(1, false, 500);
    </script>
</body>
</html>

十六、状态模式(状态模式的关键是区分事物内部的状态,事物内部状态的改变往往会带来事物的行为改变,把事物的每种状态都封装成单独的类,并将请求委托给当前的状态对象,跟此种状态有关的行为都被封装在这个类内部)

1、简单例子

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>
    <script type="text/javascript">
        function OfflightState(light) {
            this.light = light;
        }

        OfflightState.prototype.buttonWasPressed = function() {
            console.log("弱光");
            this.light.setState(this.light.weakLightState);
        };

        function WeaklightState(light) {
            this.light = light;
        }

        WeaklightState.prototype.buttonWasPressed = function() {
            console.log("强光");
            this.light.setState(this.light.strongLightState);
        };

        function StronglightState(light) {
            this.light = light;
        }

        StronglightState.prototype.buttonWasPressed = function() {
            console.log("关灯");
            this.light.setState(this.light.offLightState);
        };

        function Light() {
            this.offLightState = new OfflightState(this);
            this.weakLightState = new WeaklightState(this);
            this.strongLightState = new StronglightState(this);
            this.button = null;
        }

        Light.prototype.setState = function(newState) {
            this.curState = newState;
        };

        Light.prototype.init = function() {
            var button = document.createElement("button"),
                self = this;

            button.innerHTML = "开关";
            this.button = document.body.appendChild(button);

            this.curState = this.offLightState;

            this.button.onclick = function() {
                self.curState.buttonWasPressed();
            };
        };

        var light = new Light();
        light.init();
    </script>
</body>
</html>

2、文件上传例子

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>
    <script type="text/javascript">
        var plugin = (function() {
            var plugin = document.createElement("embed");

            plugin.style.display = "none";
            plugin.type = "application/txftn-webkit";

            plugin.sign = function() {
                console.log("开始文件扫描");
            };

            plugin.pause = function() {
                console.log("暂停文件上传");
            };

            plugin.uploading = function() {
                console.log("开始文件上传");
            };

            plugin.del = function() {
                console.log("删除文件上传");
            };

            plugin.done = function() {
                console.log("完成文件上传");
            };

            document.body.appendChild(plugin);

            return plugin;
        })();

        function Upload(fileName) {
            this.plugin = plugin;
            this.fileName = fileName;
            this.button1 = null;
            this.button2 = null;
            this.signState = new SignState(this);
            this.uploadingState = new UploadingState(this);
            this.pauseState = new PauseState(this);
            this.doneState = new DoneState(this);
            this.errorState = new ErrorState(this);
            this.currState = this.signState;
        }

        Upload.prototype.init = function() {
            this.dom = document.createElement("div");
            this.dom.innerHTML = '<span>文件名称:' + this.fileName + '</span>' + '<button data-action="button1">扫描中</button><button data-action="button2">删除</button>';
            document.body.appendChild(this.dom);

            this.button1 = this.dom.querySelector('[data-action="button1"]');
            this.button2 = this.dom.querySelector('[data-action="button2"]');

            this.bindEvent();
        };

        Upload.prototype.bindEvent = function() {
            var self = this;

            this.button1.onclick = function() {
                self.currState.clickHandler1();
            };

            this.button2.onclick = function() {
                self.currState.clickHandler2();
            };
        };

        Upload.prototype.sign = function() {
            this.plugin.sign();
            this.currState = this.signState;
        };

        Upload.prototype.uploading = function() {
            this.button1.innerHTML = "正在上传,点击暂停";
            this.plugin.uploading();
            this.currState = this.uploadingState;
        };

        Upload.prototype.pause = function() {
            this.button1.innerHTML = "已暂停,点击继续上传";
            this.plugin.pause();
            this.currState = this.pauseState;
        };

        Upload.prototype.done = function() {
            this.button1.innerHTML = "上传完成";
            this.plugin.done();
            this.currState = this.doneState;
        };

        Upload.prototype.error = function() {
            this.button1.innerHTML = "上传失败";
            this.currState = this.errorState;
        };

        Upload.prototype.del = function() {
            this.plugin.del();
            this.dom.parentNode.removeChild(this.dom);
        };

        var StateFactory = (function() {
            function State() {}

            State.prototype.clickHandler1 = function() {
                throw Error("子类必须重写父类的clickHandler1方法");
            };

            State.prototype.clickHandler2 = function() {
                throw Error("子类必须重写父类的clickHandler2方法");
            };

            return function(param) {
                function F(uploadObj) {
                    this.uploadObj = uploadObj;
                }

                F.prototype = new State();

                for (var i in param) {
                    F.prototype[i] = param[i];
                }

                return F;
            };
        })();

        var SignState = StateFactory({
            clickHandler1: function() {
                console.log("扫描中,点击无效...");
            },
            clickHandler2: function() {
                console.log("文件正在上传中,不能删除");
            }
        });

        var UploadingState = StateFactory({
            clickHandler1: function() {
                this.uploadObj.pause();
            },
            clickHandler2: function() {
                console.log("文件正在上传中,不能删除");
            }
        });

        var PauseState = StateFactory({
            clickHandler1: function() {
                this.uploadObj.uploading();
            },
            clickHandler2: function() {
                this.uploadObj.del();
            }
        });

        var DoneState = StateFactory({
            clickHandler1: function() {
                console.log("文件已完成上传,点击无效");
            },
            clickHandler2: function() {
                this.uploadObj.del();
            }
        });

        var ErrorState = StateFactory({
            clickHandler1: function() {
                console.log("文件上传失败,点击无效");
            },
            clickHandler2: function() {
                this.uploadObj.del();
            }
        });

        var uploadObj = new Upload("abc.txt");
        uploadObj.init();

        window.external.upload = function(state) {
            uploadObj[state]();
        };

        window.external.upload("sign");

        setTimeout(function() {
            window.external.upload("uploading");
        }, 1000);

        setTimeout(function() {
            window.external.upload("done");
        }, 10000);
    </script>
</body>
</html>

3、js版本的状态机

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>
    <script type="text/javascript">
        function Light() {
            this.currState = FSM.off;
            this.button = null;
        }

        Light.prototype.init = function() {
            var button = document.createElement("button"),
                self = this;

            button.innerHTML = "已关灯";
            this.button = document.body.appendChild(button);

            this.curState = this.offLightState;

            this.button.onclick = function() {
                self.currState.buttonWasPressed.call(self);
            };
        };

        var FSM = {
            off: {
                buttonWasPressed: function() {
                    console.log("关灯");
                    this.button.innerHTML = "下一次按我就是开灯";
                    this.currState = FSM.on;
                }
            },
            on: {
                buttonWasPressed: function() {
                    console.log("开灯");
                    this.button.innerHTML = "下一次按我就是关灯";
                    this.currState = FSM.off;
                }
            }
        };

        var light = new Light();
        light.init();
    </script>
</body>
</html>

© 著作权归作者所有

共有 人打赏支持
L
粉丝 18
博文 166
码字总数 116776
作品 0
成都
程序员
私信 提问

暂无文章

第11章 多线程

程序、进程、线程 程序(program)是为完成特定任务、用某种语言编写的一组指令的集合。即指一段静态的代码,静态对象。 **进程(process)**是程序的一次执行过程或是正在运行的一个程序。动...

流小文
58分钟前
4
0
SpringBoot引入第三方jar包或本地jar包的处理方式

在开发过程中有时会用到maven仓库里没有的jar包或者本地的jar包,这时没办法通过pom直接引入,那么该怎么解决呢 一般有两种方法 - 第一种是将本地jar包安装在本地maven库 - 第二种是将本地j...

独钓渔
今天
2
0
五、MyBatis缓存

一、MyBatis缓存介绍 缓存的使用可以明显的加快访问数据速度,提升程序处理性能,生活和工作中,使用缓存的地方很多。在开发过程中,从前端-->后端-->数据库等都涉及到缓存。MyBatis作为数据...

yangjianzhou
今天
2
0
最近研究如何加速UI界面开发,有点感觉了

最近在开发JFinal学院的JBolt开发平台,后端没啥说的,做各种极简使用的封装,开发者上手直接使用。 JBolt开发平台包含常用的用户、角色、权限、字典、全局配置、缓存、增删改查完整模块、电...

山东-小木
今天
3
0
《月亮与六便士》的读后感作文3000字

《月亮与六便士》的读后感作文3000字: 看完英国作家威廉.萨默塞特.毛姆所著《月亮与六便士》(李继宏译),第一疑问就是全书即没提到“月亮”,也没提到“六便士”。那这书名又与内容有什么...

原创小博客
昨天
3
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部