发布-订阅模式 又叫做观察者模式或消息机制,定义了一种依赖关系,解决了主体对象与观察者之间功能的耦合
举例
<button id='btn'>点击后弹出 alert 弹框</button> <!-- 用户点击时相当于发布者发布消息 -->
<script>
btn.addEventListener('click', () => alert('用户点击了按钮!!')); // 订阅者订阅了 btn 的click 时间
</script>
发布者 定义了 addEventListener 里的事件属性和回调函数,
订阅者 订阅了事件属性,当发布者里有对应事件type,订阅者触发时,会调用所需要的方法
代码说明
- 简易
模拟事件,拥有发布,订阅方法
class PublishSubscribeBasic {
subscribes = {}; // 保存所有的订阅事件
/**
* 订阅函数
* @param {*} subscribe 订阅的事件名
* @param {*} callback 订阅的回调函数
*/
on(subscribe, callback) {
const subscribeCallbacks = this.subscribes[subscribe] || [];
this.subscribes[subscribe] = [...subscribeCallbacks, callback];
};
/**
* 发布函数
* @param {*} subscribe 订阅的事件名
* @param {...any} args 发布时传递的参数
*/
emit(subscribe, ...args) {
const subscribeCallbacks = this.subscribes[subscribe] || [];
subscribeCallbacks.forEach(callback => callback.call(this, args));
};
};
const pubsub = new PublishSubscribeBasic();
pubsub.on('A', (args) => console.log(args)); // 订阅一个 A 事件并传递回调函数
setTimeout(() => pubsub.emit('A', '这是 A 收到的第一个参数'), 1000); // 一秒后发布 A 事件并传递参数
- 完善版本
模拟事件,拥有发布,订阅,取消订阅方法
class EevebtBus {
eventMap = new Map(); // 保存所有的订阅事件
/**
* 模拟 UUID,用于某订阅事件回调函数对应的 key
* @returns 模拟获取一个 UUID
*/
getUUID() {
return String(Date.now()) + String(Math.random());
};
/**
* 获取事件名对应的所有订阅事件,如果没有,返回一个空 Map
* @param {*} subscribe
* @returns 事件名对应的所有订阅事件的回调函数
*/
getSubscribe(subscribe){
return this.eventMap.get(subscribe) || new Map()
}
/**
* 订阅事件
* key 事件名称
* callback 回调函数
* @returns 本次订阅的标识符,用于之后取消订阅
* */
on(key,callback) {
const uid = this.getUUID();
let nowobj = this.getSubscribe(key);//初始化
nowobj.set(uid,callback);
!this.eventMap.get(key) && this.eventMap.set(key,nowobj);
return uid;
}
/**
* 发布事件
* key 事件名称
* args 入参
* */
emit(key,...args){
let nowobj = this.getSubscribe(key);//初始化
for (const [uuid, callback] of nowobj) {
callback.call(this, args);
};
}
/**
* 取消订阅
* key 事件名称
* 可以是事件名,此时会取消事件名对应的所有订阅;可以为某个订阅的 uuid,此时仅取消此个订阅
* */
remove(key){
const isSubscribe = typeof key === 'string' && this.eventMap.get(key); // 是事件名
const isUUID = !isSubscribe && typeof key === 'string';
if(isSubscribe){
this.eventMap.delete(key)
}else if(isUUID){
for (const [subscribe, subscribeCallbacks] of this.eventMap) {
for (const [uuid, callback] of subscribeCallbacks) {
if (uuid === key) {
subscribeCallbacks.delete(key);
return;
};
};
};
}
return;
}
}
const pubsub = new EevebtBus();
const A = pubsub.on('A', (args) => console.log(args)); // 订阅一个 A 事件并传递回调函数
const B = pubsub.on('B', (args) => console.log(args)); // 订阅一个 A 事件并传递回调函数
// setTimeout(() =>pubsub.remove(A), 1000); // 销毁事件
setTimeout(() =>pubsub.remove('A'), 1000); // 销毁事件
setTimeout(() =>{
pubsub.emit('A', '这是 A 收到的第一个参数')
pubsub.emit('B', '这是 B 收到的第一个参数')
}, 1000); // 一秒后发布 A 事件并传递参数