策略模式
在选择最佳策略以处理特定任务(上下文)的时候仍然保持相同的接口;
//表单验证的例子
var data = {
firs_name: "Super",
last_name: "Man",
age: "unknown",
username: "o_0"
}
validator = {
types: {},
message: [],
config: {},
validate: function(data) {
var i, msg, type, checker, result_ok;
this.message = [];
for(i in data) {
if(data.hasOwnProperty(i)) {
type = this.config[i];
checker = this.types[type];
if(!type) continue;
if(!checker) {
throw {
name: "ValidationError",
message: "No handler to validate type " + type
};
}
result_ok = checker.validate(data[i]);
if(!result_ok) {
msg = "Invalid value for *" + i + "*, " + checker.instructions;
this.message.push(msg);
}
}
}
return this.hasErrors();
},
//帮助程序
hasErrors: function() {
return this.message.length !== 0;
}
}
validator.config = {
firs_name: 'isNonEmpty',
age: 'isNumber',
username: 'isAlphaNum'
}
validator.types = {
isNonEmpty: {
validate: function (value) {
return value !== "";
},
instructions: "this value cannot be empty"
},
isNumber: {
validate: function(value) {
return !isNaN(value);
},
instructions: "the value can only be a valid number, e.g. 1, 3.14 or 2010"
},
isAlphaNum: {
validate: function(value) {
return !/[^a-z0-9]/i.test(value);
},
instructions: "the value can only contain characters and number, no special symbols"
}
}
validator.validate(data);
if(validator.hasErrors()) {
console.log(validator.message.join('
'));
}
外观模式
通过把常用方法包装到一个新的方法中,从而提供一个更为便利的API
//以处理浏览器事件为例
var myevent = {
//...
stop: function(e) {
(typeof e.preventDefault === 'function') && e.preventDefault();
(typeof e.stopPropagation === 'function') && e.stopPropagation();
//ie
(typeof e.returnVlaue === 'boolean') && (e.returnVlaue = false);
(typeof e.cancelBubble === 'boolean') && (e.cancelBubble = true);
}
}
代理模式
通过包装一个对象以控制对它的访问,其主要方法是将访问聚集为组成或仅当真正必要的时候才执行访问,从而避免了高昂的操作开销
这种模式的其中一个例子为:延迟初始化;假设初始化对象开销非常大,但客户端可能在初始化对象后从未使用它,这种时候就可以通过代理替换本体对象的接口来解决
- 首先由客户端发出一个初始化请求,然后代理以一切正常作为响应
- 但实际上却没有将该消息传递到本体对象;
- 直到客户端明显需要本体对象完成一些工作的时候,代理财经两个消息一起传递;
//视频列表例子
<p><span id="toggle-all">Toggle Checked</span></p>
<ol id="vids">
<li><input type="checkbox" checked><a href="http://new.music.yahoo.com/vides/--2158073">Gravedigger</a></li>
<li><input type="checkbox" checked><a href="http://new.music.yahoo.com/vides/--4472739">Save Me</a></li>
<li><input type="checkbox" checked><a href="http://new.music.yahoo.com/vides/--45286339">Grush</a></li>
<li><input type="checkbox" checked><a href="http://new.music.yahoo.com/vides/--2144530">Don't Drink The Water</a></li>
<li><input type="checkbox" checked><a href="http://new.music.yahoo.com/vides/--217241800">Funny the Way It Is</a></li>
<li><input type="checkbox" checked><a href="http://new.music.yahoo.com/vides/--2144532">What Would You Say</a></li>
</ol>
//未使用代理
var http = {
makeRequest: function(ids, callback) {
var url = 'http://query.yahooapis.com/v1/public/yql?q=',
sql = 'select * from music.video.id where ids IN ("%ID%")',
format = "format=json",
handler = "callback=" + callback,
script = document.createElement('script')
sql = sql.replace("%ID%", ids.join('","'));
sql = encodeURIComponent(sql);
url += sql + '&' + format + '&' + handler;
script.src = url;
document.body.appendChild(script);
}
}
var videos = {
getPlayer: function(id) {},
updateList: function(data) {},
//展开折叠信息区域
getInfo: function(id) {
var info = $("info" + id);
if(!info) {
//负责与服务器通信
http.makeRequest([id], "videos.updateList");
return;
}
if(info.style.display === "none") {
info.style.display = '';
} else {
info.style.display = 'none';
}
}
};
var $ = function (id) {
return document.getElementById(id);
};
$("vids").onclick = function(e) {
var scr ,id;
e = e || window.event;
src = e.target || e.srcElement;
//阻止跳转
if(src.nodeName !== "A") return;
if(typeof e.preventDefault === "function") e.preventDefault();
e.returnValue = false;
id = src.href.split('--')[1];
//如果是正在播放,即已经展开
if(src.className == "play") {
src.parentNode.innerHTML = videos.getPlayer(id);
return;
}
src.parentNode.id = "v" + id;
videos.getInfo(id);
};
$("toggle-all").onclick = function(e) {
var hrefs, i, max, id;
hrefs = $('vids').getElementsByTagName('a');
for(i = 0, max = hrefs.length; i < max; i += 1) {
if(hrefs[i].className === "play") continue;
if(!hrefs[i].parentNode.firstChild.checked) continue;
id = hrefs[i].href.split('--')[1];
hrefs[i].parentNode.id = '/' + id;
videos.getInfo(id);
}
}
//使用代理:就只是将http.makeRequest()改为proxy.makeRequest();
var proxy = {
ids: [],
delay: 50,
timeout: null,
callback: null,
context: null,
makeRequest: function(id, callback, context) {
//加入列队
this.ids.push(id);
this.callback = callback || function(){};
this.context = context || '';
//设置超时时间
if(!this.timeout) {
this.timeout = setTimeout(function() {
proxy.flush();
}, this.delay);
}
},
flush: function() {
http.makeRequest(this.ids, 'proxy.handler');
//清除超时设置和列队
this.timeout = null;
this.ids = [];
},
handler: function(data) {
var i, max;
//单个视频
if(data.error) return;
if(parseInt(data.query.count, 10) === 1) {
proxy.callback.call(proxy.context, data.query.results.Video);
return;
}
//多个视频
for( i = 0, max = data.query.results.Video.length; i < max; i++) {
proxy.callback.call(proxy.context, data.query.results.Video[i]);
}
}
}
缓存代理
代理可以通过将以前的请求结果缓存到新的cache属性中,从而更进一步地保护本体对象http地访问;那么如果videos对象恰好再一次请求同一个视频id,proxy可以直接从缓存中取出该信息
中介者模式
通过使对象之间相互并不直接‘通话’,而是仅通过一个中介者对象进行通信,从而促进形成松散耦合
<p>Player one press "1", player two press "0". Go! (you have half a minute...)</p>
<div id="results"></div>
//
//玩家
function Player(name) {
this.points = 0;
this.name = name;
}
Player.prototype.play = function () {
this.points += 1;
//中介者的played方法在play调用后,更新score哈希表并传送到记分板
mediator.played();
};
//记分板
var scoreboard = {
element: document.getElementById('results'),
update: function (score) {
var i, msg = '';
for (i in score) {
if (score.hasOwnProperty(i)) {
msg += '<p><strong>' + i + '</strong>: ';
msg += score[i];
msg += '</p>';
}
}
this.element.innerHTML = msg;
}
};
//中介者
var mediator = {
players: {},
setup: function () {
var players = this.players;
players.home = new Player('Home');
players.guest = new Player('Guest');
},
played: function () {
var players = this.players,
score = {
Home: players.home.points,
Guest: players.guest.points
};
scoreboard.update(score);
},
keypress: function (e) {
e = e || window.event; // IE
if (e.which === 49) { // key "1"
mediator.players.home.play();
return;
}
if (e.which === 48) { // key "0"
mediator.players.guest.play();
return;
}
}
};
//初始化中介者
mediator.setup();
window.onkeypress = mediator.keypress;
//30s
setTimeout(function () {
window.onkeypress = null;
alert('Game over!');
}, 30000);
其中记分板与玩家对象并不关联,通过中介者沟通