前言
大家好,今天简单实现一个发布-订阅模式,相信大家对这个模式都不陌生,或多或少也都见过或者听过,日常开发中,用到的地方也不少,举一个常见的例子Vue中的响应式原理中使用了发布-订阅模式
并且呢,在当前环境下的面试中也会常被问到一道题
你经常使用的设计模式有哪些,有什么作用,如何实现它?
那么今天就来聊聊发布-订阅者模式
概念
这呢里先说明一下什么是发布-订阅模式
首先呢发布-订阅模式的发布和订阅都由统一的一个调度中心来处理,那也就是说这个模式呢是有三部分组成的
- 发布者:将消息事件发布到调度中心
- 订阅者: 把自己想关注的消息事件,注册到调度中心
- 调度中心:处理事件注册与发布
有什么作用呢,就是在异步编程中实现更松的解耦
实现
先列举下需要实现发布-订阅模式的思路,目的呢就是实现三个方法,添加、删除、派发
- 需要一个事件变量对象来存储消息事件,因为呢订阅者是不止一个
- 定义一个添加方法,将事件添加到事件变量
- 定义一个删除方法,将事件于事件变量中删除
- 定义一个派发方法,调用事件变量中的事件
那我们来根据上述四条内容先来实现一个基本的架子
class EventSubscription{
constructor(){
// 事件变量对象
this.eventVarObject = {};
}
// 事件添加方法
addEventFun(){}
// 事件删除方法
deleteEventFun(){}
// 派发方法
emitEventFun(){}
}
继续来详细分析每一个方法需要做什么事情
addEventFun
addEventFun事件添加方法
- 添加消息事件是需要一个事件名的,以及消息事件的事件回调函数
- 需要将消息事件添加到事件变量中,并且不止一个变量
- 添加事件变量时需要判断是否存在当前事件,做不同的处理
那我们来继续优化addEventFun方法
/**
* 事件添加方法
* @param {'*'} name 事件名
* @param {*} callback 事件回调函数
*/
addEventFun(name, callback) {
// 判断事件变量中是否有当前事件, 如果没有的话初始化一个空数组
if (!this.eventVarObject[name]) this.eventVarObject[name] = [];
// 如果存在,那就是说明该事件多个订阅者,继续往后push
this.eventVarObject[name].push(callback);
}
deleteEventFun
deleteEventFun事件删除方法
- 删除消息事件是需要一个事件名的,以及消息事件的事件回调函数
- 事件变量中,对应的事件回调函数不止一个,需要对符合条件的删除,仅删除这个callback
- 如果事件回调函数不存在的话,直接删除事件
那我们来继续优化deleteEventFun方法
/**
* 事件删除方法
* @param {*} name 事件名
* @param {*} callback 事件回调函数
*/
function deleteEventFun(name, callback) {
// 事件回调函数不存在,删除相应整个事件
if (!callback){
delete this.eventVarObject[name];
return
};
// 需要对符合条件的删除
const index = this.eventVarObject[name].findIndex((item) => item == callback);
this.eventVarObject[name].splice(index, 1);
}
emitEventFun
emitEventFun事件派发方法
- 事件派发方法只需要事件名,确定是哪个事件,也可以传递参数
- 将事件变量中,对应的事件回调函数依次执行
那我们来继续优化emitEventFun方法
/**
* 派发方法
* @param {*} name 事件名
* @param {*} data 参数
*/
function emitEventFun(name, data) {
// 确认事件是否订阅
if (!this.eventVarObject[name]) return;
// 依次执行事件回调函数
this.eventVarObject[name].forEach((callback) => {
callback(...data);
});
}
到目前为止,整个发布-订阅模式的功能已经基本实现,还缺少一些错误逻辑的处理,在这我就不添加了
完整代码
class EventSubscription {
constructor() {
// 事件变量对象
this.eventVarObject = {};
}
/**
* 事件添加方法
* @param {'*'} name 事件名
* @param {*} callback 事件回调函数
*/
addEventFun(name, callback) {
// 判断事件变量中是否有当前事件, 如果没有的话初始化一个空数组
if (!this.eventVarObject[name]) this.eventVarObject[name] = [];
// 如果存在,那就是说明该事件多个订阅者,继续往后push
this.eventVarObject[name].push(callback);
}
/**
* 事件删除方法
* @param {'*'} name 事件名
* @param {*} callback 事件回调函数
*/
deleteEventFun(name, callback) {
// 事件回调函数不存在,删除相应整改事件
if (!callback){
delete this.eventVarObject[name];
return
};
// 需要对符合条件的删除
const index = this.eventVarObject[name].findIndex(
(item) => item == callback
);
this.eventVarObject[name].splice(index, 1);
}
/**
* 派发方法
* @param {*} name 事件名
* @param {*} data 参数
*/
emitEventFun(name, data) {
// 确认事件是否订阅
if (!this.eventVarObject[name]) return;
// 依次执行事件回调函数
this.eventVarObject[name].forEach((callback) => {
callback(...data);
});
}
}
那最后呢,来个demo来测试下是否正确
const eventSubscription = new EventSubscription();
function test1(params) {
console.log("测试test1:", params);
}
function test2(params) {
console.log("测试test2:", params);
}
// 添加订阅内容
eventSubscription.addEventFun('test', test1);
eventSubscription.addEventFun('test', test2);
console.log(eventSubscription)
// 输出结果
// EventSubscription {
// eventVarObject: { test: [ [Function: test1], [Function: test2] ] }
// }
// 派发订阅事件
eventSubscription.emitEventFun('test', 'tests事件触发')
// 输出结果
// 测试test1: tests事件触发
// 测试test2: tests事件触发
// 删除订阅事件
eventSubscription.deleteEventFun('test', test2)
console.log(eventSubscription)
// 输出结果
// EventSubscription { eventVarObject: { test: [ [Function: test1] ] } }
eventSubscription.deleteEventFun('test')
console.log(eventSubscription)
// 输出结果
// EventSubscription { eventVarObject: {} }
结语
因为呢,我在参加掘金日新计划活动,所以来请个赞,如果感觉此文稍微有些帮助的话,请不吝点个赞🥺🥺🥺,帮我加个油
开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 7 天,点击查看活动详情