发布订阅模式理解
1.发布-订阅模式
发布订阅模式是一种一对多的对象对应关系,多个观察者同时监听某一个对象,当该对象发生改变时,就会执行一个发布事件,这个发布事件会通知所有的事件订阅者,事件订阅者根据得到的数据进而改变自己的状态。
一个完整的发布订阅模式,由发布者、订阅者和消息管理器组成。

2.发布订阅模式的实现思路
1)创建一个发布者对象,并在其上添加一个列表属性,用于存放订阅者的回调函数
2)发布者发布消息,将消息依次传给每一个列表中的回调函数并触发它
简单的demo:
var shoeObj = {//存放订阅者的回调函数list:[],//将订阅者的回调函数放在列表中listen(){shoeObj.list.push(fn);},//发布消息trigger(){for(var i = 0,fn; fn = this.list[i++];) {//将参数应用于每一个回调函数fn.apply(this,arguments); }}
};
//小红的订阅数据
shoeObj.listen(function(color,size){console.log("颜色是:"+color);console.log("大小是:"+size);
})
//小明的订阅数据
shoeObj.listen(function(color,size){console.log(123,color,size);
})
//发布数据
shoeObj.trigger("红色",40);
shoeObj.trigger("黑色",42);
输出结果:

增加key:让用户只接收自己感兴趣的信息
var shoeObj = {//存放订阅者的回调函数list:[],//将订阅者的回调函数放在列表中listen(key,fn){//为每一个key创建一个列表if (!this.list[key]){this.list[key]=[];}shoeObj.list[key].push(fn);},//发布消息,将信息发布给每一个回调函数trigger(){var key = Array.prototype.shift.call(arguments); // 取出消息类型名称var fns = this.list[key]; // 取出该key对应的回调函数的集合// 如果没有订阅过该消息的话,则返回if(!fns || fns.length === 0) {return;}for(var i = 0,fn; fn = fns[i++];) {//将参数应用于每一个回调函数fn.apply(this,arguments); }}}; //小红的订阅数据,key是‘red’shoeObj.listen('red',function(size){console.log("大小是:"+size);})//小明的订阅数据,key是‘black’shoeObj.listen('black',function(size){console.log(123,size);})//发布消息shoeObj.trigger("red",40);shoeObj.trigger("black",42);
输出结果:
这样,订阅者就只订阅了自己感兴趣的信息了。
将代码进行封装,可以应用于各种场景:
var event = {//存放订阅者的回调函数list:[],//将订阅者的回调函数放在列表中listen(key,fn){//为每一个key创建一个列表if (!this.list[key]){this.list[key]=[];}this.list[key].push(fn);},//发布消息,将信息发布给每一个回调函数trigger(){var key = Array.prototype.shift.call(arguments); // 取出消息类型名称var fns = this.list[key]; // 取出该key对应的回调函数的集合// 如果没有订阅过该消息的话,则返回if(!fns || fns.length === 0) {return;}for(var i = 0,fn; fn = fns[i++];) {//将参数应用于每一个回调函数fn.apply(this,arguments); }}}; //初始化普通对象,使其具有发布订阅功能var initEvent = function(obj) {for(var i in event) {obj[i] = event[i];}};
加入取消订阅:
var event = {//存放订阅者的回调函数list:[],//将订阅者的回调函数放在列表中listen(key,fn){//为每一个key创建一个列表if (!this.list[key]){this.list[key]=[];}this.list[key].push(fn);},//发布消息,将信息发布给每一个回调函数trigger(){var key = Array.prototype.shift.call(arguments); // 取出消息类型名称var fns = this.list[key]; // 取出该key对应的回调函数的集合// 如果没有订阅过该消息的话,则返回if(!fns || fns.length === 0) {return;}for(var i = 0,fn; fn = fns[i++];) {//将参数应用于每一个回调函数fn.apply(this,arguments); }},//取消订阅remove(key,fn){var fns = this.list[key];// 如果key对应的消息没有订阅过的话,则返回if(!fns) {return false;}// 如果没有传入具体的回调函数,表示需要取消key对应消息的所有订阅if(!fn) {fn && (fns.length = 0);}else {for(var i = fns.length - 1; i >= 0; i--) {var _fn = fns[i];if(_fn === fn) {fns.splice(i,1); // 删除订阅者的回调函数}}}}}; //初始化普通对象,使其具有发布订阅功能var initEvent = function(obj) {for(var i in event) {obj[i] = event[i];}};
封装发布订阅模式:
//发布订阅模式
class eventEmitter(){constructor(){this.list = {};}listen(key,fn){if(!this.list[key]){this.list[key] = [];}this.list[key].push(fn);}publish(){let key = Array.prototype.shift.call(arguments);let fns = this.list[key];if (!fns||fns.length===0){return ;}for(let i=0;i<fns.length;i++){fns[i].apply(this,arguments);}}remove(key,fn){let fns = this.list[key];if (!fns || fns.length === 0){return ;}if (!fn){this.list[key]=[];} else {let fnsFilter = fns.filter((it,ind)=>{return it != fn;})this.list[key]=fnsFilter;}}}
export default new eventEmitter();
3.为什么要用发布订阅模式?
如果一个数据或者事件的变化会对很多事件产生影响,比如我们在初始化接口之后会得到初始化数据,当初始化接口返回成功时,需要执行其他的几个方法,正常情况下,我们会注册一个函数,在这个函数中写n个函数的执行,但是如果利用发布订阅模式,其他几个函数可以订阅该接口的成功事件,然后根据订阅接收到的数据执行自己的函数。
优点:耦合性低,便于代码的维护
参考博客:https://www.cnblogs.com/itgezhu/p/10947405.html



















