模式一:单体模式
作用:保证一个特定类仅有一个实例,常作为解决某个特定问题而存在的通用且共享(多处方法可使用,作为引用对象)的对象
javascript实现单体问题:每次new一个实例对象后,实例对象之间并不相等,两者为不同对象,非单体对象
实现:
①静态属性实例
在Universe构造函数的静态属性中缓存单个实例(this当前对象)
代码实现:
function Universe(){
if(typeof Universe.instance ==="object"){
return Universe.instance;
}
this.start_num = 0;
this.nam = "su";
Universe.instance = this;
return this // 隐式返回
}
var u1 = new Universe();
var u2 = new Universe();
u1=== u2 // true
缺点:外部可修改instance属性
②闭包中实例(闭包来保护单个实例,保护私有变量)
重写构造函数,通过闭包访问私有变量
代码实现:
function Universe(){
var instance = this;
this.start_num = 0;
this.nam = "su";
Universe=function(){
return instance;
}
}
var u1 = new Universe();
var u2 = new Universe();
u1=== u2 // true
不足:uni.constructor的构造函数与 Universe()的不同了
解决:重置构造函数指针
模式二:工厂模式
作用:为客户提供一种创建对象的接口,根据输入类型(传参)不同而创建不同的对象,创建的对象为子对象,都继承父对象思想
实现:
①自定义实现:
function CarMaker(){}
CarMaker.prototype.driver = function(){
console.log("我有" + this.doors + "个门");
return "我有" + this.doors + "个门";
}
// 静态 构造函数
CarMaker.factory = function(type){
var constr = type;
var newCar;
// 如果 构造 函数 不存在 ,则发生错误
if(typeof CarMaker[constr] != "function") {
throw{
name: "Error",
message: constr + "doesn`t exit!!"
}
}
// 原型继承父类 ,仅继承一次
if(typeof CarMaker[constr].prototype.driver !== "function"){
CarMaker[constr].prototype = new CarMaker(); // 重点 :发生了继承
}
// 创建一个新的实例
newCar = new CarMaker[constr]();
return newCar; // 返回新实例
}
CarMaker.SUV = function(){
this.doors = 4;
}
CarMaker.compact = function(){
this.doors = 10;
}
CarMaker.convertible = function(){
this.doors = 8;
}
// 使用
var SUV = CarMaker.factory('SUV');
var compact = CarMaker.factory('compact');
var convertible = CarMaker.factory('convertible');
SUV.driver();
compact.driver();
convertible.driver();
// 继承 实例新子对象 返回新子对象
②系统内置对象工厂new Object()
var o = new Object();
var n = new Object(1);
var s = new Object('1');
var b = new Object(true);
o.constructor === Object // true;
n.constructor === Numer// true;
s.constructor === String// true;
b.constructor === Boolean// true;
模式三:迭代模式
作用:为相关对象提供操作数据结构的API,比如列表结构的操作
实现:
var egg = (function(){
var index = 0;
var data = [1,2,3,4,5];
var length = data.length;
return {
next : function(){
var element;
if(!this.hasNext){
return null;
}
element = data[index];
index = index + 2;
return element;
},
hasNext : function(){
return index < length;
}
};
}());
// 使用, 遍历迭代遍历数据结构
while(agg.hasNext()){
console.log(agg.next);
}
模式四:装饰者模式
作用: 通过继承和重载来实现对象的功能扩展
实现:
function Sale(price){
this.price = price || 100;
}
Sale.prototype.getPrice = function(){
return this.price;
}
Sale.decorators = {};
Sale.decorators.fedtax = { // 将作为newObj实现继承并重载getPrice
getPrice : function(){
var price = this.uber.getPrice(); // this.uber 为 : newObj.uber = F.prototype;
price += price * 5 /100;
return price;
}
}
Sale.decorators.quebec = {
getPrice : function(){
var price = this.uber.getPrice();
price += price * 7.5 /100;
return price;
}
}
Sale.decorators.money = {
getPrice : function(){
return "$" + this.uber.getPrice().toFixed(2);
}
}
Sale.decorators.cdn = {
getPrice : function(){
return "CND$" + this.uber.getPrice().toFixed(2);
}
}
Sale.prototype.decorate = function(decorator){
var F = function(){};
var overrides = this.constructor.decorators[decorator];
var i,newObj;
F.prototype = this;
newObj = new F();
newObj.uber = F.prototype;
for(i in overrides){
if(overrides.hasOwnProperty(i)){ // 将所有非原型属性 继承给 newObj
newObj[i] = overrides[i]
}
}
return newObj;
}
// 用法
var sale = new Sale(100);
sale = sale.decorate('fedtax');
console.log(sale.getPrice());
模式五:策略模式
作用:支持在运行时选择算法,代码的客户端可以使用同一个接口来工作(实例中的validate接口),但是它确实根据客户正在试图执行任务的上下文来从多个算法中选择一个用于处理特定任务的算法(根据config里的设置来执行types里的上下文 validate)。为一个对象提供多种策略方法,供自由选择.
应用场景:表单验证时常用
实现:
// 策略模式
// 现在有一组数据 要做检查
var validator = {
types : {}, // 所有可用的 检查函数 validator.types[xxx]
config : {}, // 检查格式
messages : [], // 错误消息
validate : function(data){ // 接口方法, data为数据源对象
var type;
var msg;
var checker;
var result_ok;
// 重置所有消息
this.messages = [];
for(key in data){
if(data.hasOwnProperty(key)){ // 遍历所有私有属性
type = this.config[key]; // config下的所有检查方法
checker = this.types[type]; // validator.types.[type]
if(!type){ //config里未定义的话,直接跳过
continue;
}// end type
if(!checker){
throw {
name:"e",
messages : "es" +type
}
} // end !checker
result_ok = checker.validate(data[key]);
if(!result_ok){ // 如果验证失败
msg = key + "数据类型无效,该值必须是" + checker.intructions;
this.messages.push(msg);
}
}
}
return this.hasErrors();
},
hasErrors: function(){
return this.messages.length !== 0;
}
};
var studentData = {
age : 'sd', // 错误示范
desc : 12
};
//设置这组数据的 检查格式 对应函数
validator.config = {
age : 'isNumber',
desc : 'isString'
};
// 定义 检查函数
validator.types.isNumber = {
validate : function(data){
return !isNaN(data);
},
intructions : "数据必须是一个数字类型"
};
validator.types.isString = {
validate : function(data){
return (typeof data === "string")
},
intructions : "数据必须是一个字符类型"
};
// 将数据放入 '检查总函数' 中
validator.validate(studentData);
// 如果发生错误,那么 输出错误
if(validator.hasErrors()){
console.log(validator.messages.join("\n"));
}
// 错误示范输出:
// age数据类型无效,该值必须是数据必须是一个数字类型
// desc数据类型无效,该值必须是数据必须是一个字符类型
模式六:外观模式
作用:为对象提供可供选择的接口,用于包装旧对象使其兼容性更高
应用场景:浏览器之间兼容的API差异,比如IE(return false)和FF,chrome(preventDefault)的取消默认行为API不一样,通过外观模式包装成统一的stop() API既可将差异隐藏在外观之后。同时,还适用于重新设计和重构对象,在原有的对象上创建一个外观,将其取代旧代码既可。
实现:
var myEvent = {
stop : function(e){
e.preventDefault();
e.stopPropagation();
}
//
};
模式七:代理模式
作用:一个对象充当另一个对象的接口,介于对象的客户端和对象本体之间(客户端、服务器),对该对象的访问进行了保护,试图使对象本体做更少的工作,最大化节约性能。
应用场景 : 在web应用中,http请求的合并和网络数据的缓存,例子 点击标题展开视频内容(视频内容由服务端获取)..
实现:
// 定义一个proxy代理对象
//proxy对象接管 http和videos之间的通信
// proxy对象 用一个50ms的延迟来合并该段时间内的所有请求
//proxy建立一个空队列以收集50ms接收到的视频id,然后排空该队列,同时还调用
//-> http并向其提供回调函数。
var proxy = {
ids : [],
delay : 50,
timeout : null,
callback : null,
context : null,
makeRequest : function(id,callback,context){
// 加入到队列中
this.ids.push(id);
this.callback = callback;
this.context = context;
//设置超时时间
if(!this.timeout){
this.timeout = setTimeout(function(){
proxy.flush();
},this.delay);
}
},
flush : function(){
http.makeRequests(this.ids,"proxy.handler");
// 清除超时设置和队列
this.timeout = null;
this.ids = [];
},
handler:function(data){
var i,max;
//单个视频
if(parseInt(data.query.count,10) === 1){
proxy.callback.call(proxy.context,data.query.results.Video);//调用且绑定上下文到proxy.context
return;
}
//多个视频
for(var i= 0,max = data.query.results.Video.length;i < max;i++){
proxy.callback.call(proxy.context,data.query.results.Video[i]);
}
}
};
模式八:中介者模式
作用:为了让应用程序里对象与对象之间的解耦,使得程序的可维护性更高。应用程序都是由大大小小的对象构成的,对象之间需要通信,一旦程序规模扩大,对象增多,对象之间的耦合度就会越高 ,对象间的耦合度越高,程序的可维护性会越来越差(修改一个对象将会影响许多个对象).
应用场景 : 多应用于双(多)人(对象)交互的场景,比如游戏...
实现:(传统实现: 两个对象间的交互 ,模式实现:对象之间加入一个中介者(Mediator)来为两个对象提供通信)
// 例子 : 双人点击记分游戏
//参与的对象 : 玩家(玩家1+玩家2)、计分板、中介者
// 玩家 通知 中介者 , 中介者 处理 将其返回给 玩家
function Player(name){ // 玩家对象
this.name = name;
this.point = 0;
}
Player.prototype.play = function(){
this.point +=1;
mediator.played(); // 通知 中介者
}
var scoreboad = { // 计分板对象
element : document.getElementById('scoreBox'),
update : function(score){
var i;
var msg = '';
for(i in score){
if(score.hasOwnProperty(i)){
msg = i + " : " + score[i] + "/n"; // 显示分数对象 主场 : 23
}
}
this.element.innerHTML = msg;
}
};
// 接下来便是中介者对象
var mediator = {
players : {},//将所有玩家 放置在这个全局对象中
setup : function(){
var players = this.players;
players.home = new Player('主');
players.guest = new Player('客');
},
played : function(){
var players = this.players;
var score = {
Home : players.home.points,
Guest : players.guest.points
};
scoreboad.update(score);//将分数返回给计分板,计分板负责显示
},
keypress : function(e){
if(e.which === 49){ //玩家1按键1
mediator.players.home.play();
return;
}
if(e.which === 48){ //玩家2按键0
mediator.players.guest.play();
return;
}
}
};
// 运行
mediator.setup();
window.onkeypress = mediator.keypress;
// 游戏30秒后结束
setTimeout(function(){
window.onkeypress = null;
alert('游戏结束');
},30000);
注:代理模式与中介者模式好似差不多,那么它们之间的区别是什么?
答:代理是在对象的权限范围内进行操作,也就是只针对一个对象,而中介的话,是全权负责处理,可以针对多个对象
模式九:观察者模式
作用:广泛应用于客户端编程中,比如事件的操作就是模式的例子。在应用程序里,并不是一个对象调用另一个对象,而是一个对象订阅另一个对象的特定活动并在状态改变后获得通知(回调). 比如window对象订阅一个click点击事件
应用场景 : 异步编程
实现:
1、杂志订阅
2、按键游戏