设计模式-javascript实现【发布-订阅模式】
发布-订阅模式也叫观察者模式,可以用在异步编程中替代回调函数的方案。发布-订阅模式可以取代对象之间硬
编码的通知机制,一个对象不用显式地调用另一个对象的某个接口。发布-订阅模式让两个对象松耦合地联系在
一起,虽然不太清除彼此的细节,但这不影响它们之间相互通信。
1. 发布-订阅模式的实现思路
- 指定发布者
- 给发布者添加一个缓存列表,用于存放回调函数以便通知订阅者
- 最后发布消息的时候,发布者会遍历这个缓存列表,依次触发里面存放的订阅者回调函数
2. 实现通用发布-订阅模式
创建一个通用的事件发布订阅对象,其他对象可以基于此对象继承其订阅发布功能
// 定义通用的发布订阅对象
const event = {
clientList: {},
// 不能使用箭头函数定义方法,this会自动绑定到event对象, 无法指向新创建的对象
listen: function (key, fn) {
if(!this.clientList[key]) {
this.clientList[key] = [];
}
// 将订阅消息添加缓存列表
this.clientList[key].push(fn);
},
trigger: function (key, ...payload) {
const fns = this.clientList[key];
if(!fns || fns.length == []) return false;
fns.forEach((fn) => {
fn.apply(this, payload);
});
}
};
// 通过继承的方式创建发布者
const salesOffice = Object.create(event);
// 订阅发布者的事件
salesOffice.listen('sale80', (price) => {
console.log(`price is ${price}`);
});
// 发布消息
salesOffice.trigger('sale80', '100w');
3. 创建全局发布-订阅对象
利用全局发布订阅Event对象,我们可以在两个封装良好的模块中进行通信,这两个模块可以完全不知道对方的存在。
class Event {
constructor(){
this.clientList = {};
}
// 订阅消息
listen (key, fn) {
if(!this.clientList[key]) {
this.clientList[key] = [];
}
// 将订阅消息添加缓存列表
this.clientList[key].push(fn);
// 返回handler对象,供取消订阅使用
return {key, fn};
}
// 发布事件
trigger (key, ...payload) {
const fns = this.clientList[key];
if(!fns || fns.length == []) return false;
fns.forEach((fn) => {
fn.apply(this, payload);
});
}
// 取消订阅
remove (handler) {
const {key, fn} = handler;
const fns = this.clientList[key];
if(!fns) return false;
if(!fn) {
fns && (fns.length = 0); // 清空事件回调函数
}
else {
for(let [i,value] of fns.entries()) {
if(value === fn){
fns.splice(i, 1);
break;
}
}
}
}
}
const event = new Event();
// 观察者订阅事件
const handler = event.listen('sale80', (price) => {
console.log(`price is ${price}`);
});
// 生产者发布消息
event.trigger('sale80', '100w');
// 观察者取消订阅
event.remove(handler);
event.trigger('sale80', '120w');