自定义事件总线
文章目录
- 什么是自定义事件总线
- 具体实现
- 思路分析
- 定义结构
- 实现 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开源库单次发送和接受的数据不能超过…...
后进先出(LIFO)详解
LIFO 是 Last In, First Out 的缩写,中文译为后进先出。这是一种数据结构的工作原则,类似于一摞盘子或一叠书本: 最后放进去的元素最先出来 -想象往筒状容器里放盘子: (1)你放进的最后一个盘子(…...
【入坑系列】TiDB 强制索引在不同库下不生效问题
文章目录 背景SQL 优化情况线上SQL运行情况分析怀疑1:执行计划绑定问题?尝试:SHOW WARNINGS 查看警告探索 TiDB 的 USE_INDEX 写法Hint 不生效问题排查解决参考背景 项目中使用 TiDB 数据库,并对 SQL 进行优化了,添加了强制索引。 UAT 环境已经生效,但 PROD 环境强制索…...
线程与协程
1. 线程与协程 1.1. “函数调用级别”的切换、上下文切换 1. 函数调用级别的切换 “函数调用级别的切换”是指:像函数调用/返回一样轻量地完成任务切换。 举例说明: 当你在程序中写一个函数调用: funcA() 然后 funcA 执行完后返回&…...
聊聊 Pulsar:Producer 源码解析
一、前言 Apache Pulsar 是一个企业级的开源分布式消息传递平台,以其高性能、可扩展性和存储计算分离架构在消息队列和流处理领域独树一帜。在 Pulsar 的核心架构中,Producer(生产者) 是连接客户端应用与消息队列的第一步。生产者…...
测试markdown--肇兴
day1: 1、去程:7:04 --11:32高铁 高铁右转上售票大厅2楼,穿过候车厅下一楼,上大巴车 ¥10/人 **2、到达:**12点多到达寨子,买门票,美团/抖音:¥78人 3、中饭&a…...
项目部署到Linux上时遇到的错误(Redis,MySQL,无法正确连接,地址占用问题)
Redis无法正确连接 在运行jar包时出现了这样的错误 查询得知问题核心在于Redis连接失败,具体原因是客户端发送了密码认证请求,但Redis服务器未设置密码 1.为Redis设置密码(匹配客户端配置) 步骤: 1).修…...
【数据分析】R版IntelliGenes用于生物标志物发现的可解释机器学习
禁止商业或二改转载,仅供自学使用,侵权必究,如需截取部分内容请后台联系作者! 文章目录 介绍流程步骤1. 输入数据2. 特征选择3. 模型训练4. I-Genes 评分计算5. 输出结果 IntelliGenesR 安装包1. 特征选择2. 模型训练和评估3. I-Genes 评分计…...
【7色560页】职场可视化逻辑图高级数据分析PPT模版
7种色调职场工作汇报PPT,橙蓝、黑红、红蓝、蓝橙灰、浅蓝、浅绿、深蓝七种色调模版 【7色560页】职场可视化逻辑图高级数据分析PPT模版:职场可视化逻辑图分析PPT模版https://pan.quark.cn/s/78aeabbd92d1...
无人机侦测与反制技术的进展与应用
国家电网无人机侦测与反制技术的进展与应用 引言 随着无人机(无人驾驶飞行器,UAV)技术的快速发展,其在商业、娱乐和军事领域的广泛应用带来了新的安全挑战。特别是对于关键基础设施如电力系统,无人机的“黑飞”&…...
如何更改默认 Crontab 编辑器 ?
在 Linux 领域中,crontab 是您可能经常遇到的一个术语。这个实用程序在类 unix 操作系统上可用,用于调度在预定义时间和间隔自动执行的任务。这对管理员和高级用户非常有益,允许他们自动执行各种系统任务。 编辑 Crontab 文件通常使用文本编…...
