自定义事件总线
文章目录
- 什么是自定义事件总线
- 具体实现
- 思路分析
- 定义结构
- 实现 on
- 实现 emit
- 实现 off
- 源码
什么是自定义事件总线
- 自定义事件总线属于一种观察着模式,其中包括三个角色
- 发布者(Publisher):发出事件(Event)
- 订阅者(Subscriber):订阅事件(Event),并且会进行响应(Handler)
- 事件总线(EvnetBus):无论是发布者还是订阅者都是通过事件总线作为中台的
具体实现
思路分析
-
事件总线相信大家都用过或者听过,特别是在 vue2 中,这就是一种组件传值的方式,轻量又简单
-
从使用上来说,通过 on 监听事件,同时可以使用 emit 来发送事件,当然也可也通过 off 来取消事件
-
使用还是非常简单的,但是怎么才能实现 emit 触发 on 监听的呢,竟然要触发的话,首先我是不是需要有一个地方可以存储这个通过 on 监听的方法呢?存储了之后,就是使用,emit 实际就是通过参数找到对应的存储方法,然后我们帮他手动调用一下
-
所以基于这一点,我们就可以得到三个点,on、store、emit,如图:
定义结构
-
根据上面的分析,我们可以选用 class 的形式来实现,
-
其次我们还需要 on emit off 三个方法,如下:
class JcEventBus{constructor() {}on(){}emit(){}off(){} }
-
在这之中,我们就按照分析,确定一下 store,在 class 结构中,可以 this 访问数据和方法,所以在 JcEventBus 初始化时,就可以定义一个变量来存储后续的方法,而可以通过一个字符串准确存储和取出的话,对象和map都可以,但是这里对象会更加好操作,所以改造后 constructor 代码如下:
constructor() {this.eventBus = {} }
实现 on
-
on 方法一般是有两个参数的,我们可以写出基础的函数结构,如下:
on(eventName, callback){}
-
我们不妨在思考一下,有时候这个回调函数的this,可能需要我们来指定,方便在函数内部使用 this,所以为了实现这一点,一般会给 callback 传递一个普通函数而非匿名函数,同时应该有第三个参数来接受这个 this,如下:
on(eventName, callback, ctx){}
-
确定好参数之后,我们来进行一下具体的实现,首先假设监听的名称为 foo,而且监听的函数有时候可能不止一个,而存储多个函数我们可以想到什么,数组,利用数组来实现存储这些函数,需要用的时候在找到对应的函数进行执行即可,确定好这一点之后,我们要做的就是以 eventName 为 key,callback 存储在数组中为 value,{ foo: [fn1,fn2…] },代码如下:
on(eventName, callback, ctx){// 如果不存在则初始化为空数组const handles = this.eventBus[eventName] || []// 传递一个对象,同时保存 执行的函数 和 thishandles.push({ callback, ctx })// 存储在 store 中this.eventBus[eventName] = handles }
-
我们来通过实例化 JcEventBus 来进行测试一下,看看是否真的存入了,测试代码如下:
eventBus.on('foo',function (...payload) {console.log('foo 函数,参数为:', payload)},obj )eventBus.on('bar',function (...payload) {console.log('bar 函数,参数为:', payload)},obj )console.log(eventBus.eventBus)
-
输出如图:
实现 emit
-
emit 要做的事件也非常简单,传递事件名称和参数即可,因此基础的函数结构如下:
emit(eventName, ...payload){}
-
参数可能会有多个,使用剩余参数来接收,这个方法,第一步就是要查找有没有这个方法,如果有,就执行这个事件名称上绑定的所有函数,代码如下:
emit(eventName, ...payload) {const handles = this.eventBus[eventName]if (!handles || !handles.length) returnhandles.forEach(handle => {// 使用 apply 绑定 thishandle.callback.apply(handle.ctx, payload)}) }
-
这个应该非常简单吧,直接上测试代码。如下:
const eventBus = new JcEventBus()const obj = { name: 'coderjc' }eventBus.on('foo',function (...payload) {console.log('foo1 函数,参数为:', payload, this)},obj )eventBus.on('foo',function (...payload) {console.log('foo2 函数,参数为:', payload)},obj )eventBus.emit('foo', 1, 2, 3)
-
结果如图:
实现 off
-
off 类似于 removeEventListener 这个方法,也是事件名 + 函数,即可取消,所以函数基础结构如下:
off(eventName, callback){}
-
然后就是通过事件名称找到这个数组,在这个数组里面找到对应的函数进行删除即可,不过这里会会有一点细节需要注意,比如一个函数被存储了多次的情况下,当然这个你可以在存储的时候就拦截,避免重复,但是我们这里没有,所以如果删除就要删除多个,而这个删除可能有部分的朋友们就会想到遍历,全等判断,然后删除就行吗,这个也可以,但是可能需要多一点的操作,我们先看看这个思路删除会有什么结果,我写了一个demo,如下:
// 这里用数字代替函数 const arr = [1, 2, 2, 4, 5, 6] const val = 2for (let i = 0; i < arr.length; i++) {if (arr[i] === val) {// 删除arr.splice(i, 1)} } console.log(arr)
-
结果如图:
-
不知道发现了没有,只删除了其中的一个2,还有一个没有删除,这是因为 splice 方法删除之后,原数组的长度就-1了,此时原来索引为2的数组就会变成索引为1,而 i 的值又没有同步的 -1,就会直接跳过,所以就是这个结果了所以解决方法很简单,同时 i-1 即可,当然还是有其他方法的,都是非常简单的
-
我这里就直接过滤数组重新赋值了,如下:
off(eventName, callback){const handles = this.eventBus[eventName]if (!handles || !handles.length) returnthis.eventBus[eventName] = handles.filter(handle => handle.callback !== callback) }
-
来看看是不是真的有用,测试代码如下:
const eventBus = new JcEventBus()function foo(...payload) {console.log('foo1 函数,参数为:', payload) }eventBus.on('foo', foo)eventBus.on('foo', function (...payload) {console.log('foo2 函数,参数为:', payload) })// 移除 eventBus.off('foo', foo)eventBus.emit('foo', 1, 2, 3)
-
结果如图:
源码
当然了,最后我是加了一些对于参数的类型判断,非常简单,就不单独介绍了
function _verifyType(eventName = null, callback = null) {if (eventName && typeof eventName !== 'string') {throw new Error('eventName must be a string')}if (callback && typeof callback !== 'function') {throw new Error('callback must be a function')}
}class JcEventBus {constructor() {this.eventBus = {}}on(eventName, callback, ctx) {_verifyType(eventName, callback)const handles = this.eventBus[eventName] || []handles.push({ callback, ctx })this.eventBus[eventName] = handles}off(eventName, callback) {_verifyType(eventName, callback)const handles = this.eventBus[eventName]if (!handles || !handles.length) returnthis.eventBus[eventName] = handles.filter(handle => handle.callback !== callback)}emit(eventName, ...payload) {_verifyType(eventName)const handles = this.eventBus[eventName]if (!handles || !handles.length) returnhandles.forEach(handle => {handle.callback.apply(handle.ctx, payload)})}
}
相关文章:

自定义事件总线
文章目录 什么是自定义事件总线具体实现思路分析定义结构实现 on实现 emit实现 off 源码 什么是自定义事件总线 自定义事件总线属于一种观察着模式,其中包括三个角色发布者(Publisher):发出事件(Event)订阅…...
212.【2023年华为OD机试真题(C卷)】堆内存申请(排序和贪心算法-JavaPythonC++JS实现)
🚀点击这里可直接跳转到本专栏,可查阅顶置最新的华为OD机试宝典~ 本专栏所有题目均包含优质解题思路,高质量解题代码(Java&Python&C++&JS分别实现),详细代码讲解,助你深入学习,深度掌握! 文章目录 一. 题目-堆内存申请二.解题思路三.题解代码Python题解代…...

Flink Watermark和时间语义
Flink 中的时间语义 时间语义: EventTime:事件创建时间;Ingestion Time:数据进入Flink的时间;Processing Time:执行操作算子的本地系统时间,与机器无关。不同的时间语义有不同的应用场合&#x…...
HarmonyOS UI框架简介
HarmonyOS UI框架介绍 HarmonyOSUI框架是一个用于构建跨设备应用的开发框架,它属于HarmonyOS系统架构的上层框架。该框架通过提供一系列的开发模型、声明式UI范式、系统API等,帮助开发者更高效地构建用户界面。 在HarmonyOSUI框架中,开发语…...

编程羔手解决Maven引入多个版本的依赖包,导致包冲突了
最近升级了些依赖发现有个hutool的方法老报错,java.lang.NoSuchMethodError: cn.hutool.core.util.ObjectUtil.defaultIfNull(Ljava/lang/Object;Ljava/util/function/Supplier;) 在 Maven 项目中,当不同的依赖模块引入 Hutool 的不同版本时,…...

C#,入门教程(08)——基本数据类型及使用的基础知识
上一篇: C#,入门教程(07)——软件项目的源文件与目录结构https://blog.csdn.net/beijinghorn/article/details/124139947 数据类型用于指定数据体(DataEntity,包括但不限于类或结构体的属性、变量、常量、函数返回值)…...

分类预测 | Matlab实现DBO-SVM蜣螂算法优化支持向量机多特征分类预测
分类预测 | Matlab实现DBO-SVM蜣螂算法优化支持向量机多特征分类预测 目录 分类预测 | Matlab实现DBO-SVM蜣螂算法优化支持向量机多特征分类预测分类效果基本描述程序设计参考资料 分类效果 基本描述 1.Matlab实现DBO-SVM蜣螂算法优化支持向量机多特征分类预测(完整…...
计算机二级Python选择题考点——公共基础部分
计算机完成一条指令所花费的时间称为一个指令周期。(指令周期越短,指令执行就越快)顺序程序不具有并发性。(具有顺序性、封闭性和可再现性)结构化程序设计强调程序的易读性。系统软件:操作系统、编译程序、数据库管理系统 应用软件:杀毒软件在…...

《微机原理与应用》期末考试题库(附答案解析)
第1章 微型计算机概述 1.微型计算机的硬件系统包括___A _____。 A.控制器、运算器、存储器和输入输出设备 B.控制器、主机、键盘和显示器 C.主机、电源、CPU和输入输出 D.CPU、键盘、显示器和打印机 2.微处…...

如何在Android Glide中结合使用CenterCrop和自定义圆角变换(图片部分圆角矩形)
如何在Android Glide中结合使用CenterCrop和自定义圆角变换(图片部分圆角矩形) 在Android开发中,使用Glide加载图片时,我们经常需要对图片进行特定的处理,比如裁剪和圆角变换,特别是一些设计稿,…...
华为机考-手拍球游戏
【手拍手计算次数和总数】游戏规则:左手和右手拍球初始数为0,首先左手第一次拍球数1下,右手拍球1下,接下来左手在拍球时是上一次左手上一次右手的总和,右手也是上一次左手上一次右手拍球的总和,最后拍球总数…...
【线上问题】两台服务器的时间不一致导致jwt解析错误
目录 一、问题描述二、解决方法 一、问题描述 1.线上生产问题,本地和测试环境均无问题 2.本地和测试由于网关和登录服务均在同一台机器 3.线上的登录服务和网关部署不在一起,登录服务的时间正常,网关服务的服务器时间比实际快5秒 4.登录服务j…...

58.网游逆向分析与插件开发-游戏增加自动化助手接口-游戏菜单文字资源读取的逆向分析
内容来源于:易道云信息技术研究院VIP课 之前的内容:接管游戏的自动药水设定功能-CSDN博客 码云地址(master分支):https://gitee.com/dye_your_fingers/sro_-ex.git 码云版本号:34b9c1d43b512d0b4a3c395b…...

Vue-2、初识Vue
1、helloword小案列 代码 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>初始Vue</title><!--引入vue--><script type"text/javascript" src"https://cdn.jsdelivr.n…...

机器学习项目标记图像数据 - 安装LabelImg及功能介绍
什么是LabelImg? LabelImg 是一款流行的图像标注工具,主要用于计算机视觉领域。它允许用户为机器学习项目标记图像数据,特别是用于训练目标检测模型。 如何安装LabelImg pip install PyQt5 pip install pyqt5-tools pip install lxml pip …...
12.15 log 122.买卖股票的最佳时机 II,55. 跳跃游戏
122.买卖股票的最佳时机 II class Solution { public:int maxProfit(vector<int>& prices) {int result0;for(int i0;i<prices.size();i){if(i>0&&prices[i]-prices[i-1]>0){resultprices[i]-prices[i-1];}}return result;} }; 这道题贪心贪的时每…...

Redis - 挖矿病毒 db0 库 backup 反复出现解决方案
问题描述 腾讯云的服务器,使用 Docker 部署了 Redis 之后,发现 DB0 中总是出现 4 条 key,分别是 backup01backup02backup03backup04 而自己每次存入 db0 中的数据过一会就会被无缘无故删除掉。 原因分析 挖矿病毒 解决方案 在启动的时候…...

LiveGBS流媒体平台GB/T28181常见问题-国标编号是什么设备编号和通道国标编号标记唯一的摄像头|视频|镜头通道
LiveGBS国标GB28181中国标编号是什么设备编号和通道国标编号标记唯一的摄像头|视频|镜头通道 1、什么是国标编号?2、国标设备ID和通道ID3、ID 统一编码规则4、搭建GB28181视频直播平台 1、什么是国标编号? 国标GB28181对接过程中,可能有的小…...

Unity ShaderGraph 技能冷却转圈效果
Unity ShaderGraph 技能冷却转圈效果 前言项目场景布置代码编写ShaderGraph 连线总结 参考 前言 遇到一个需求,要展示技能冷却的圆形遮罩效果。 项目 场景布置 代码编写 Shader核心的就两句 // 将uv坐标系的原点移到纹理中心 float2 uv i.uv - float2(0.5, 0…...

C++上位软件通过Snap7开源库访问西门子S7-1200/S7-1500数据块的方法
前言 本人一直从事C上位软件开发工作较多,在之前的项目中通过C访问西门子PLC S7-200/S7-1200/S7-1500并进行数据交互的应用中一直使用的是ModbusTCP/ModbusRTU协议进行。Modbus上位开源库采用的LibModbus。经过实际应用发现Modbus开源库单次发送和接受的数据不能超过…...
【Java学习笔记】Arrays类
Arrays 类 1. 导入包:import java.util.Arrays 2. 常用方法一览表 方法描述Arrays.toString()返回数组的字符串形式Arrays.sort()排序(自然排序和定制排序)Arrays.binarySearch()通过二分搜索法进行查找(前提:数组是…...

YSYX学习记录(八)
C语言,练习0: 先创建一个文件夹,我用的是物理机: 安装build-essential 练习1: 我注释掉了 #include <stdio.h> 出现下面错误 在你的文本编辑器中打开ex1文件,随机修改或删除一部分,之后…...
蓝桥杯 2024 15届国赛 A组 儿童节快乐
P10576 [蓝桥杯 2024 国 A] 儿童节快乐 题目描述 五彩斑斓的气球在蓝天下悠然飘荡,轻快的音乐在耳边持续回荡,小朋友们手牵着手一同畅快欢笑。在这样一片安乐祥和的氛围下,六一来了。 今天是六一儿童节,小蓝老师为了让大家在节…...
使用van-uploader 的UI组件,结合vue2如何实现图片上传组件的封装
以下是基于 vant-ui(适配 Vue2 版本 )实现截图中照片上传预览、删除功能,并封装成可复用组件的完整代码,包含样式和逻辑实现,可直接在 Vue2 项目中使用: 1. 封装的图片上传组件 ImageUploader.vue <te…...

【单片机期末】单片机系统设计
主要内容:系统状态机,系统时基,系统需求分析,系统构建,系统状态流图 一、题目要求 二、绘制系统状态流图 题目:根据上述描述绘制系统状态流图,注明状态转移条件及方向。 三、利用定时器产生时…...
Unit 1 深度强化学习简介
Deep RL Course ——Unit 1 Introduction 从理论和实践层面深入学习深度强化学习。学会使用知名的深度强化学习库,例如 Stable Baselines3、RL Baselines3 Zoo、Sample Factory 和 CleanRL。在独特的环境中训练智能体,比如 SnowballFight、Huggy the Do…...

【开发技术】.Net使用FFmpeg视频特定帧上绘制内容
目录 一、目的 二、解决方案 2.1 什么是FFmpeg 2.2 FFmpeg主要功能 2.3 使用Xabe.FFmpeg调用FFmpeg功能 2.4 使用 FFmpeg 的 drawbox 滤镜来绘制 ROI 三、总结 一、目的 当前市场上有很多目标检测智能识别的相关算法,当前调用一个医疗行业的AI识别算法后返回…...

C/C++ 中附加包含目录、附加库目录与附加依赖项详解
在 C/C 编程的编译和链接过程中,附加包含目录、附加库目录和附加依赖项是三个至关重要的设置,它们相互配合,确保程序能够正确引用外部资源并顺利构建。虽然在学习过程中,这些概念容易让人混淆,但深入理解它们的作用和联…...
站群服务器的应用场景都有哪些?
站群服务器主要是为了多个网站的托管和管理所设计的,可以通过集中管理和高效资源的分配,来支持多个独立的网站同时运行,让每一个网站都可以分配到独立的IP地址,避免出现IP关联的风险,用户还可以通过控制面板进行管理功…...

从 GreenPlum 到镜舟数据库:杭银消费金融湖仓一体转型实践
作者:吴岐诗,杭银消费金融大数据应用开发工程师 本文整理自杭银消费金融大数据应用开发工程师在StarRocks Summit Asia 2024的分享 引言:融合数据湖与数仓的创新之路 在数字金融时代,数据已成为金融机构的核心竞争力。杭银消费金…...