翼度科技»论坛 编程开发 JavaScript 查看内容

[JS] 事件总线

10

主题

10

帖子

30

积分

新手上路

Rank: 1

积分
30
事件总线与发布订阅模式

事件总线是对发布-订阅模式的一种实现。
发布-订阅模式定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知。
发布-订阅模式实现了松耦合,发布者不是直接将消息发送给订阅者,而是经过了一个中间的代理,事件总线就是一种中间代理的实现。
事件总线维护了一个事件列表,订阅者可以订阅某一个事件,并指定一个回调(回调的具体实现在订阅者内部);
每个事件又维护了一个依赖列表,发布者可以“触发”一个事件,事件总线负责遍历该事件的依赖列表,调用每一个当初订阅者订阅时指定的回调函数。
在 JS 中实现Event Bus

定义一个EventBus类:
  1. class EventBus{}
复制代码
需要维护一个事件列表,在初始化事件总线对象的时候创建。
对于每一个事件,我们需要记录它的事件名(string类型),还需要记录该事件的依赖列表(Array类型),依赖列表其实就是各个订阅者的回调函数的列表。
这采用了一个对象来记录多个事件,刚好键值对就是事件名:依赖列表
  1. constructor(){
  2.     this.eventObject = {};
  3. }
复制代码
实现订阅
每一次订阅需要指定订阅的事件名发布时要触发的回调函数

  • 如果指定的事件不存在,则添加一个事件,并推入该新依赖(回调函数)。
  • 如果指定的事件存在,则直接推入新依赖(回调函数)
  1. subscribe(eventName, callback){
  2.     if(!this.eventObject[eventName]){
  3.         this.eventObject[eventName] = [];
  4.     }
  5.     this.eventObject[eventName].push(callback);
  6. }
复制代码
实现发布
发布也要考虑到指定的事件是否存在。如果不存在,则中断并返回警告;如果存在指定事件,则依次调用事件的依赖列表(回调列表)。
  1. publish(eventName){
  2.     const callbackList = this.eventObject[eventName];
  3.     if(!callbackList)return console.warn(eventName + " Not Found!");
  4.     for(let callback of callbackList){
  5.         callback();
  6.     }
  7. }
复制代码
汇总如下
  1. class EventBus{
  2.     constructor(){
  3.         this.eventObject = {};
  4.     }
  5.     /**
  6.      * @param {string} eventName
  7.     */
  8.     publish(eventName){
  9.         const callbackList = this.eventObject[eventName];
  10.         if(!callbackList)return console.warn(eventName + " Not Found!");
  11.         
  12.         for(let callback of callbackList){
  13.             callback();
  14.         }
  15.     }
  16.     /**
  17.      * @param {string} eventName
  18.      * @param {Function} callback
  19.     */
  20.     subscribe(eventName, callback){
  21.         if(!this.eventObject[eventName]){
  22.             this.eventObject[eventName] = [];
  23.         }
  24.         this.eventObject[eventName].push(callback);
  25.     }
  26. }
复制代码
优化Event Bus的实现

在发布时传递参数

使用...args介绍不定长参数列表,在发布时传入,并在调用回调函数列表的时候依次传入。
  1. /**
  2. * @param {string} eventName
  3. */
  4. publish(eventName, ...args){
  5.     const callbackList = this.eventObject[eventName];
  6.     if(!callbackList)return console.warn(eventName + " Not Found!");
  7.     for(let callback of callbackList){
  8.         callback(...args);
  9.     }
  10. }
复制代码
提供取消订阅的操作

在订阅者调用subscribe方法订阅事件的时候,返回一个用于取消订阅的unSubscribe方法。
在实现事件的回调函数列表的时候,需要为每一个回调函数添加一个id,方便以后查询并删除该回调函数。
这里将订阅的回调函数列表换成用对象结构存储,因为在数组中删除某个中间元素较麻烦且耗时,效率不如对象结构的delete删除键值对。
换成对象结构存储后,键值对表示:id:回调函数。
  1. class EventBus{
  2.     constructor(){
  3.         // 初始化事件列表
  4.         this.eventObject = {};
  5.         // 回调函数列表的 id
  6.         this.callbackId = 0;
  7.     }
  8.     /**
  9.      * @param {string} eventName
  10.     */
  11.     publish(eventName, ...args){
  12.         // 取出该事件的回调函数列表(对象)
  13.         const callbackObject = this.eventObject[eventName];
  14.         if(!callbackObject)return console.warn(eventName + " Not Found!");
  15.         
  16.         // 执行每一个回调函数,这里的id是对象的key
  17.         for(let id in callbackObject){
  18.             callbackObject[id](...args);
  19.         }
  20.     }
  21.     /**
  22.      * @param {string} eventName
  23.      * @param {Function} callback
  24.      * @returns {{
  25.      *      unSubscribe: Function
  26.      * }}
  27.     */
  28.     subscribe(eventName, callback){
  29.         if(!this.eventObject[eventName]){
  30.             this.eventObject[eventName] = {};
  31.         }
  32.         // 为当前事务的回调函数申请一个专属id
  33.         const id = this.callbackId++;
  34.         // 绑定回调函数
  35.         this.eventObject[eventName][id] = callback;
  36.         // 生成取消订阅的函数
  37.         const unSubscribe = ()=>{
  38.             // 删除该回调函数
  39.             delete this.eventObject[eventName][id];
  40.             // 如果该事件的回调函数都删完了,则顺便删除事件列表中的事件
  41.             if(Object.keys(this.eventObject[eventName]).length === 0){
  42.                 delete this.eventObject[eventName];
  43.             }
  44.         }
  45.         
  46.         return {unSubscribe};
  47.     }
  48. }
复制代码
清除某个事件
  1. /**
  2. * @description 清除某事件
  3. * @param {string} eventName
  4. */
  5. clear(eventName){
  6.     if(!this.eventObject.hasOwnProperty(eventName)){
  7.         return console.warn(eventName + " Not Found!");
  8.     }
  9.     delete this.eventObject[eventName];
  10. }
复制代码
来源:https://www.cnblogs.com/feixianxing/p/js-event-bus-publish-subscribe-pattern.html
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x

举报 回复 使用道具