发布-订阅模式又叫观察者模式,它定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,全部依赖于它的对象都将获得通知。JavaScript开发中咱们通常用事件模型来代替传统的发布-订阅模式前端
小明最近喜欢上吃老北京烧饼,但是到了卖烧饼的地方发现已经卖完了,并且排队的人还不少.幸运的是卖烧饼那个MM看小明长得帅,告诉小明等一会就有烧饼吃啦!但是小明如今还有约会要去,不知道烧饼能何时出锅,总不能由于吃烧饼而不去约会吧!这时候小明灵机一动,说烧饼MM把你电话给我吧!我先去忙,等会打电话问你烧饼好了没有。烧饼MM也没想太多,把电话给小明了。后来小龙也来买烧饼,状况跟小明差很少,小龙也把烧饼MM的电话要走了。但是问题就这来了,小明、小龙一下子打一个电话给烧饼MM,致使烧饼MM很烦,辞职走了不干了。
经过上边的事情咱们能够发现,存在好多问题
第一:卖烧饼的MM应该充当发布者
第二:小明小龙的电话应该保存在卖烧饼的用户列表中,若是卖烧饼的MM离职,这用户就会丢失
第三:实际上没有这么笨蛋的销售方式的编程
卖烧饼的店主能够把小明、小龙的电话记录下来,等店里有烧饼了在通知小龙小明来拿这就是所谓的发布-订阅模式,代码以下:设计模式
/*烧饼店*/
var Sesamecakeshop={
clienlist:[],//缓存列表
addlisten:function(fn){//增长订阅者
this.clienlist.push(fn);
},
trigger:function(){//发布消息
for(var i=0,fn;fn=this.clienlist[i++];){
fn.apply(this,arguments);
}
}
}
/*小明发布订阅*/
Sesamecakeshop.addlisten(function(price,taste){
console.log("小明发布的"+price+"元,"+taste+"味道的");
});
/*小龙发布订阅*/
Sesamecakeshop.addlisten(function(price,taste){
console.log("小龙发布的"+price+"元,"+taste+"味道的");
});
Sesamecakeshop.trigger(10,"椒盐");复制代码
从代码中能够看出,只有小明,小龙预约了烧饼,烧饼店就能够发布消息告诉小龙与小明。可是有个问题不知道你们发现了没有。小明只喜欢椒盐味道的。而小龙只喜欢焦糖味道的。上面的代码就知足不了客户的需求,给客户一种感受就是,无论我喜欢不喜欢,你都会发给我。若是发布比较多,客户就会感到厌烦,甚至会想删除订阅。下边是对代码进行改良你们能够看看。缓存
/*烧饼店*/
var Sesamecakeshop={
clienlist:{},/*缓存列表*/
/**
* 增长订阅者
* @key {String} 类型
* @fn {Function} 回掉函数
* */
addlisten:function(key,fn){
if(!this.clienlist[key]){
this.clienlist[key]=[];
}
this.clienlist[key].push(fn);
},
/**
* 发布消息
* */
trigger:function(){
var key=[].shift.call(arguments),//取出消息类型
fns=this.clienlist[key];//取出该类型的对应的消息集合
if(!fns || fns.length===0){
return false;
}
for(var i=0,fn;fn=fns[i++];){
fn.apply(this,arguments);
}
},
/**
* 删除订阅
* @key {String} 类型
* @fn {Function} 回掉函数
* */
remove:function(key,fn){
var fns=this.clienlist[key];//取出该类型的对应的消息集合
if(!fns){//若是对应的key没有订阅直接返回
return false;
}
if(!fn){//若是没有传入具体的回掉,则表示须要取消全部订阅
fns && (fns.length=0);
}else{
for(var l=fns.length-1;l>=0;l--){//遍历回掉函数列表
if(fn===fns[l]){
fns.splice(l,1);//删除订阅者的回掉
}
}
}
}
}
/*小明发布订阅*/
Sesamecakeshop.addlisten("焦糖",fn1=function(price,taste){
console.log("小明发布的"+price+"元,"+taste+"味道的");
});
/*小龙发布订阅*/
Sesamecakeshop.addlisten("椒盐",function(price,taste){
console.log("小龙发布的"+price+"元,"+taste+"味道的");
});
Sesamecakeshop.trigger("椒盐",10,"椒盐");
Sesamecakeshop.remove("焦糖",fn1);//注意这里是按照地址引用的。若是传入匿名函数则删除不了
Sesamecakeshop.trigger("焦糖",40,"焦糖");复制代码
删除的时候须要注意的是,若是订阅的时候传递的是匿名函数,删除的时候若是传入的也是匿名函数。则删除不了。由于删除时候是按照地址引用删除的。传进去的两个匿名函数,对应的地址引用是不一样的。bash
好比我们常见的用户身份分别有不一样的功能,超级管理员拥有最高权限,能够删除修改任意用户。而普通用户则只能修改本身的帐户信息。首先是用户身份验证,验证经过以后对应功能才能够显示。前端工程师
//登陆发布-订阅模式
login={
clienlist:{},/*缓存列表*/
/**
* 增长订阅者
* @key {String} 类型
* @fn {Function} 回掉函数
* */
addlisten:function(key,fn){
if(!this.clienlist[key]){
this.clienlist[key]=[];
}
this.clienlist[key].push(fn);
},
/**
* 发布消息
* */
trigger:function(){
var key=[].shift.call(arguments),//取出消息类型
fns=this.clienlist[key];//取出该类型的对应的消息集合
if(!fns || fns.length===0){
return false;
}
for(var i=0,fn;fn=fns[i++];){
fn.apply(this,arguments);
}
}
}
//超级管理员修改全部用户
var editall=(function(){
login.addlisten("loginsucc",function(data){
editall.setview(data);
});
return{
setview:function(data){
console.log(data);
console.log("超级管理员修改全部用户");
}
}
})();
//仅仅修改本身
var editOwn=(function(){
login.addlisten("loginsucc",function(data){
editOwn.setview(data);
});
return{
setview:function(data){
console.log(data);
console.log("仅仅修改本身");
}
}
})();复制代码
发布-订阅模式简单封装架构
var _Event=(function(){
var clienlist={},
addlisten,trigger,remove;
/**
* 增长订阅者
* @key {String} 类型
* @fn {Function} 回掉函数
* */
addlisten=function(key,fn){
if(!clienlist[key]){
clienlist[key]=[];
}
clienlist[key].push(fn);
};
/**
* 发布消息
* */
trigger=function(){
var key=[].shift.call(arguments),//取出消息类型
fns=clienlist[key];//取出该类型的对应的消息集合
if(!fns || fns.length===0){
return false;
}
for(var i=0,fn;fn=fns[i++];){
fn.apply(this,arguments);
}
};
/**
* 删除订阅
* @key {String} 类型
* @fn {Function} 回掉函数
* */
remove=function(key,fn){
var fns=clienlist[key];//取出该类型的对应的消息集合
if(!fns){//若是对应的key没有订阅直接返回
return false;
}
if(!fn){//若是没有传入具体的回掉,则表示须要取消全部订阅
fns && (fns.length=0);
}else{
for(var l=fns.length-1;l>=0;l--){//遍历回掉函数列表
if(fn===fns[l]){
fns.splice(l,1);//删除订阅者的回掉
}
}
}
};
return{
addlisten:addlisten,
trigger:trigger,
remove:remove
}
})();
_Event.addlisten("jianbing",function(d,all){
console.log("发布的消息来自:"+d+",具体信息:"+all);
});
_Event.addlisten("jianbing",function(d,all){
console.log("发布的消息来自:"+d+",具体信息:"+all);
})
_Event.trigger("jianbing","小小坤","前端工程师,擅长JavaScript,喜欢结交更多的前端技术人员,欢迎喜欢技术的你加QQ群:198303871")复制代码
发布-订阅模式就是常说的观察者模式,在实际开发中很是有用。它的优势是为时间是解耦,为对象之间解构,它的应用很是普遍,既能够在异步编程中也能够帮助咱们完成更松的解耦。发布-订阅模式还能够帮助咱们实现设计模式,从架构上来看,不管MVC仍是MVVC都少不了发布-订阅模式的参与。然而发布-订阅模式也存在一些缺点,建立订阅自己会消耗必定的时间与内存,也许当你订阅一个消息以后,以后可能就不会发生。发布-订阅模式虽然它弱化了对象与对象之间的关系,可是若是过分使用,对象与对象的必要联系就会被深埋,会致使程序难以跟踪与维护。app