var myImage = (function() {
var imgNode = document.createElement('img');
document.body.appendChild(imgNode);
return {
setSrc: function(src) {
imgNode.src = src;
}
}
})();
var proxyImage = (function() {
var img = new Image;
img.onload = function() {
//等真正图片加载好了之后,再改变成真实图片
myImage.setSrc(this.src);
}
return {
setSrc: function(src) {
//先设置一张缓存图片
myImage.setSrc('img/loading.gif');
img.src = src;
}
}
})();
proxyImage.setSrc('img/1.jpg');
var synchronousFile = function(id) {
console.log('开始同步文件, id 为: ' + id);
};
var proxySynchronousFile = (function() {
var cache = [], // 保存一段时间内需要同步的 ID
timer; // 定时器
return function(id) {
cache.push(id);
if(timer) { // 保证不会覆盖已经启动的定时器
return;
}
timer = setTimeout(function() {
synchronousFile(cache.join(',')); // 2 秒后向本体发送需要同
clearTimeout(timer); // 清空定时器
timer = null;
cache.length = 0; // 清空 ID 集合
}, 2000);
}
})();
var checkbox = document.getElementsByTagName('input');
for(var i = 0, c; c = checkbox[i++];) {
c.onclick = function() {
if(this.checked === true) {
proxySynchronousFile(this.id);
}
}
};
###责任链模式
一系列可能会处理请求的对象被连接成一条链,请求在这些对象之间依次传递,直到遇到一个可以处理它的对象。请求发送者只需要知道链中的第一个节点。ps:可以将业务代码从if/else中释放出来,便于程序的扩展。
业务例子:公司针对支付过定金的用户有一定的优惠政策。在正式购买后,已经支付过 500 元定金的用户会收到 100 元的商城优惠券, 200 元定金的用户可以收到 50 元的优惠券,而之前没有支付定金的用户只能进入普通购买模式,也就是没有优惠券,且在库存有限的情况下不一定保证能买到。
/*职责链 */
/* orderType:表示订单类型(定金用户或者普通购买用户), code 的值为 1 的时候是 500 元
定金用户,为 2 的时候是 200 元定金用户,为 3 的时候是普通购买用户。
pay:表示用户是否已经支付定金,值为 true 或者 false, 虽然用户已经下过 500 元定金的
订单,但如果他一直没有支付定金,现在只能降级进入普通购买模式。
stock:表示当前用于普通购买的手机库存数量,已经支付过 500 元或者 200 元定金的用
户不受此限制
*/
var order500 = function(orderType, pay, stock) {
if(orderType === 1 && pay === true) {
console.log('500 元定金预购,得到 100 优惠券');
} else {
return 'nextSuccessor'; // 我不知道下一个节点是谁,反正把请求往后面传递
}
};
var order200 = function(orderType, pay, stock) {
if(orderType === 2 && pay === true) {
console.log('200 元定金预购,得到 50 优惠券');
} else {
return 'nextSuccessor'; // 我不知道下一个节点是谁,反正把请求往后面传递
}
};
var orderNormal = function(orderType, pay, stock) {
if(stock > 0) {
console.log('普通购买,无优惠券');
} else {
console.log('手机库存不足');
}
};
var Chain = function(fn) {
this.fn = fn;
this.successor = null;
};
Chain.prototype.setNextSuccessor = function(successor) {
return 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 ); // 输出: 500 元定金预购,得到 100 优惠券
chainOrder500.passRequest( 2, true, 500 ); // 输出: 200 元定金预购,得到 50 优惠券
chainOrder500.passRequest( 3, true, 500 ); // 输出:普通购买,无优惠券
chainOrder500.passRequest( 1, false, 0 ); // 输出:手机库存不足
var goods = { // 手机库存
"red|32G": 3,
"red|16G": 0,
"blue|32G": 1,
"blue|16G": 6
};
var mediator = (function() {
var colorSelect = document.getElementById('colorSelect'),
memorySelect = document.getElementById('memorySelect'),
numberInput = document.getElementById('numberInput'),
colorInfo = document.getElementById('colorInfo'),
memoryInfo = document.getElementById('memoryInfo'),
numberInfo = document.getElementById('numberInfo'),
nextBtn = document.getElementById('nextBtn');
return {
changed: function(obj) {
var color = colorSelect.value, // 颜色
memory = memorySelect.value, // 内存
number = numberInput.value, // 数量
stock = goods[color + '|' + memory]; // 颜色和内存对应的手机库存数量
if(obj === colorSelect) { // 如果改变的是选择颜色下拉框
colorInfo.innerHTML = color;
} else if(obj === memorySelect) {
memoryInfo.innerHTML = memory;
} else if(obj === numberInput) {
numberInfo.innerHTML = number;
}
if(!color) {
nextBtn.disabled = true;
nextBtn.innerHTML = '请选择手机颜色';
return;
}
if(!memory) {
nextBtn.disabled = true;
nextBtn.innerHTML = '请选择内存大小';
return;
}
if(((number - 0) | 0) !== number - 0) { // 输入购买数量是否为正整数
nextBtn.disabled = true;
nextBtn.innerHTML = '请输入正确的购买数量';
return;
}
nextBtn.disabled = false;
nextBtn.innerHTML = '放入购物车';
}
}
})();
// 事件函数:
colorSelect.onchange = function() {
mediator.changed(this);
};
memorySelect.onchange = function() {
mediator.changed(this);
};
numberInput.oninput = function() {
mediator.changed(this);
};
适用场景:一个网站,有好几个模块都需要获取用户信息后,进行渲染显示。在未用此种设计模式之前,我们会想着在获取用户信息的ajax成功回调函数中,写代码,渲染各个模块。但是这样做会导致,我们新增一个模块,需要去修改之前ajax的回调函数,加入新增部分的处理。而一个团队中,可能ajax回调函数部分是同事A做的,而新增模块是同事B负责的,其实B具体要做什么事情,A并不需要知道,A只需要告诉你,我已经拿到用户信息了,你们想干嘛就干嘛吧。那么这时候A提供出一个接口供其他需要它的人来订阅,在A完成任务后,告诉之前这些跟它打过招呼的模块,我把这些对你们有用信息传给你们,具体后续的事情由你们自己来决定。这就是发布-订阅模式。
###组合模式
这种模式非常适用于树形结构
/******************************* Folder ******************************/
var Folder = function (name) {
this.name = name;
this.files = [];
};
Folder.prototype.add = function (file) {
this.files.push(file);
};
Folder.prototype.scan = function () {
console.log('开始扫描文件夹: ' + this.name);
for (var i = 0, file, files = this.files; file = files[i++];) {
file.scan();
}
};
/******************************* File ******************************/
var File = function (name) {
this.name = name;
};
File.prototype.add = function () {
throw new Error('文件下面不能再添加文件');
};
File.prototype.scan = function () {
console.log('开始扫描文件: ' + this.name);
};
var folder = new Folder('学习资料');
var folder1 = new Folder('JavaScript');
var folder2 = new Folder('jQuery');
var file1 = new File('JavaScript 设计模式与开发实践');
var file2 = new File('精通 jQuery');
var file3 = new File('重构与模式')
folder1.add(file1);
folder2.add(file2);
folder.add(folder1);
folder.add(folder2);
folder.add(file3);
var folder3 = new Folder('Nodejs');
var file4 = new File('深入浅出 Node.js');
folder3.add(file4);
var file5 = new File('JavaScript 语言精髓与编程实践');
folder.add(folder3);
folder.add(file5);
folder.scan();
###装饰者模式
window.onload = function() {
alert(1);
}
var _onload = window.onload || function() {};
window.onload = function() {
_onload();
alert(2);
}
但是上面会存在一些问题。1、 例如丢失了this指向。2、 需维护_onload 这个中间变量,如果函数的装饰链够长,所需的中间变量越多。
Function.prototype.before = function(beforefn) {
var __self = this; // 保存原函数的引用
return function() { // 返回包含了原函数和新函数的"代理"函数
beforefn.apply(this, arguments); // 执行新函数,且保证 this 不被劫持,新函数接受的参数
// 也会被原封不动地传入原函数,新函数在原函数之前执行
return __self.apply(this, arguments); // 执行原函数并返回原函数的执行结果,
// 并且保证 this 不被劫持
}
}
Function.prototype.after = function(afterfn) {
var __self = this;
return function() {
var ret = __self.apply(this, arguments);
afterfn.apply(this, arguments);
return ret;
}
};