准备知识: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); //true3.惰性单例(需要的时候才创建单例,不是在初始化页面时创建单例)
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();
}