发布订阅模式理解
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