設計模式
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>
2、工廠模式
定義、提供一個通用的接口來批量生產一類類似對象,咱們能夠指定咱們所但願的工廠對象的類型
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>
3、單例模式
定義:保證一個類只有一個實例,並提供一個訪問它的全局訪問點。
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>
4、策略模式
定義:定義一系列的算法,把他們一個個封裝起來,而且使他們能夠相互替換。
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>
5、代理模式(通常代理對象要實現本體對象全部的接口)
定義:爲一個對象提供一個代用品或佔位符,以便控制對它的訪問。
一、小明送花例子(經過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>
三、虛擬代理合並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>
三、緩存代理
<!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>
6、迭代器模式
定義:提供一種方法順序訪問一個聚合對象中的各個元素,而又不須要暴露該對象的內部表示。
一、內部迭代器(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>
二、外部迭代器(必須顯示的請求迭代下一個元素,加強了迭代的靈活性,能夠手動控制迭代過程或者順序)
<!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>
7、發佈-訂閱模式(觀察者模式)
定義:定義對象間的一種一對多的依賴關係,當一個對象的狀態發生改變時,全部倚賴於它的對象都將獲得通知。咱們通常用事件模型來替代傳統的發佈-訂閱模式。
一、簡單例子
<!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>
二、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>
8、命令模式(解耦命令請求者與命令接受者之間的強耦合關係,當咱們不知道命令接受者是誰,以及被請求的操做是什麼時,適合用命令模式;命令模式中的命令指的是一個執行某些特定事情的指令)
一、閉包實現模式
<!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>
9、中介者模式(解除對象與對象之間的緊耦合關係,全部的相關對象都經過中介者對象來通訊,將多多的的網狀關係解耦成一對多關係)
一、利用發佈-訂閱模式實現中介者模式,將中介者實現爲訂閱者,通訊對象實現爲發佈者,當通訊對象狀態發生改變,便推送消息給中介者,中介者處理消息並通知其餘通訊對象。
<!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>
二、利用普通對象實現中介者模式,只須要中介者開放一些接受通訊者消息的接口。
<!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>
10、適配器模式(解決兩個軟件實體間的接口不兼容的問題,使用適配器模式以後,本來因爲接口不兼容而不能工做的兩個軟件實體能夠一塊兒工做,適配器模式更多的是一種補救措施)
一、實例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>
11、裝飾者模式(給對象動態的增長職責)
一、簡單例子
<!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>
二、用裝飾者模式擴展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>
三、利用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>
12、組合模式(用小的子對象來構建更大的對象)
一、宏命令與組合模式
宏命令:一組命令的集合,經過執行宏命令的方式能夠一次執行一批命令
<!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>
十3、模板方法模式(是一種只需使用繼承就能夠實現的很是簡單的模式,由兩部分構成,抽象父類和具體的實現子類,父類封裝了子類的算法框架和執行順序,是爲了解決相同的行爲在各個子類中重複實現的問題)
一、利用原型繼承咖啡與茶的例子
<!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>
二、利用高階函數咖啡與茶的例子
<!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>
十4、享元模式(是一種用於性能優化的模式,享元模式的核心是運用共享技術來有效支持大量細粒度的對象,若是系統中由於建立了大量相似的對象致使內存佔用太高,享元模式就很是有用了,享元模式要求將對象的屬性劃分爲內部狀態和外部狀態,內部狀態通常是不會改變的能夠共享的狀態,外部狀態是改變的狀態)
一、文件上傳例子
<!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>
十5、職責鏈模式(使多個對象都有機會處理請求,從而避免請求的發送者和接受者之間的耦合關係,將這些對象連成一條鏈,並沿着這條鏈傳遞該請求,直到有一個對象處理它爲止)
一、例子一
<!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>
二、用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>
十6、狀態模式(狀態模式的關鍵是區分事物內部的狀態,事物內部狀態的改變每每會帶來事物的行爲改變,把事物的每種狀態都封裝成單獨的類,並將請求委託給當前的狀態對象,跟此種狀態有關的行爲都被封裝在這個類內部)
一、簡單例子
<!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>
二、文件上傳例子
<!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>
三、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>