自定义事件总线

文章目录

    • 什么是自定义事件总线
    • 具体实现
      • 思路分析
      • 定义结构
      • 实现 on
      • 实现 emit
      • 实现 off
    • 源码

什么是自定义事件总线

  1. 自定义事件总线属于一种观察着模式,其中包括三个角色
  2. 发布者(Publisher):发出事件(Event)
  3. 订阅者(Subscriber):订阅事件(Event),并且会进行响应(Handler)
  4. 事件总线(EvnetBus):无论是发布者还是订阅者都是通过事件总线作为中台的

具体实现

思路分析

  1. 事件总线相信大家都用过或者听过,特别是在 vue2 中,这就是一种组件传值的方式,轻量又简单

  2. 从使用上来说,通过 on 监听事件,同时可以使用 emit 来发送事件,当然也可也通过 off 来取消事件

  3. 使用还是非常简单的,但是怎么才能实现 emit 触发 on 监听的呢,竟然要触发的话,首先我是不是需要有一个地方可以存储这个通过 on 监听的方法呢?存储了之后,就是使用,emit 实际就是通过参数找到对应的存储方法,然后我们帮他手动调用一下

  4. 所以基于这一点,我们就可以得到三个点,on、store、emit,如图:

    在这里插入图片描述

定义结构

  1. 根据上面的分析,我们可以选用 class 的形式来实现,

  2. 其次我们还需要 on emit off 三个方法,如下:

    class JcEventBus{
        constructor() {
            
    	}
        
        on(){}
        
        emit(){}
        
        off(){}
    }
    
  3. 在这之中,我们就按照分析,确定一下 store,在 class 结构中,可以 this 访问数据和方法,所以在 JcEventBus 初始化时,就可以定义一个变量来存储后续的方法,而可以通过一个字符串准确存储和取出的话,对象和map都可以,但是这里对象会更加好操作,所以改造后 constructor 代码如下:

    constructor() {
    	this.eventBus = {}
    }
    

实现 on

  1. on 方法一般是有两个参数的,我们可以写出基础的函数结构,如下:

    on(eventName, callback){}
    
  2. 我们不妨在思考一下,有时候这个回调函数的this,可能需要我们来指定,方便在函数内部使用 this,所以为了实现这一点,一般会给 callback 传递一个普通函数而非匿名函数,同时应该有第三个参数来接受这个 this,如下:

    on(eventName, callback, ctx){}
    
  3. 确定好参数之后,我们来进行一下具体的实现,首先假设监听的名称为 foo,而且监听的函数有时候可能不止一个,而存储多个函数我们可以想到什么,数组,利用数组来实现存储这些函数,需要用的时候在找到对应的函数进行执行即可,确定好这一点之后,我们要做的就是以 eventName 为 key,callback 存储在数组中为 value,{ foo: [fn1,fn2…] },代码如下:

    on(eventName, callback, ctx){
        // 如果不存在则初始化为空数组
        const handles = this.eventBus[eventName] || []
        
        // 传递一个对象,同时保存 执行的函数 和 this
    	handles.push({ callback, ctx })
        
        // 存储在 store 中
    	this.eventBus[eventName] = handles
    }
    
  4. 我们来通过实例化 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)
    
  5. 输出如图:

    在这里插入图片描述

实现 emit

  1. emit 要做的事件也非常简单,传递事件名称和参数即可,因此基础的函数结构如下:

    emit(eventName, ...payload){}
    
  2. 参数可能会有多个,使用剩余参数来接收,这个方法,第一步就是要查找有没有这个方法,如果有,就执行这个事件名称上绑定的所有函数,代码如下:

    emit(eventName, ...payload) {
    	const handles = this.eventBus[eventName]
    	if (!handles || !handles.length) return
    
    	handles.forEach(handle => {
            // 使用 apply 绑定 this
    		handle.callback.apply(handle.ctx, payload)
    	})
    }
    
  3. 这个应该非常简单吧,直接上测试代码。如下:

    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)
    
  4. 结果如图:

    在这里插入图片描述

实现 off

  1. off 类似于 removeEventListener 这个方法,也是事件名 + 函数,即可取消,所以函数基础结构如下:

    off(eventName, callback){}
    
  2. 然后就是通过事件名称找到这个数组,在这个数组里面找到对应的函数进行删除即可,不过这里会会有一点细节需要注意,比如一个函数被存储了多次的情况下,当然这个你可以在存储的时候就拦截,避免重复,但是我们这里没有,所以如果删除就要删除多个,而这个删除可能有部分的朋友们就会想到遍历,全等判断,然后删除就行吗,这个也可以,但是可能需要多一点的操作,我们先看看这个思路删除会有什么结果,我写了一个demo,如下:

    // 这里用数字代替函数
    const arr = [1, 2, 2, 4, 5, 6]
    const val = 2
    
    for (let i = 0; i < arr.length; i++) {
    	if (arr[i] === val) {
    		// 删除
    		arr.splice(i, 1)
    	}
    }
    console.log(arr)
    
  3. 结果如图:

    在这里插入图片描述

  4. 不知道发现了没有,只删除了其中的一个2,还有一个没有删除,这是因为 splice 方法删除之后,原数组的长度就-1了,此时原来索引为2的数组就会变成索引为1,而 i 的值又没有同步的 -1,就会直接跳过,所以就是这个结果了所以解决方法很简单,同时 i-1 即可,当然还是有其他方法的,都是非常简单的

  5. 我这里就直接过滤数组重新赋值了,如下:

    off(eventName, callback){
        const handles = this.eventBus[eventName]
    	if (!handles || !handles.length) return
        
        this.eventBus[eventName] = handles.filter(handle => handle.callback !== callback)
    }
    
  6. 来看看是不是真的有用,测试代码如下:

    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)
    
  7. 结果如图:

    在这里插入图片描述

源码

当然了,最后我是加了一些对于参数的类型判断,非常简单,就不单独介绍了

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) return

		this.eventBus[eventName] = handles.filter(handle => handle.callback !== callback)
	}

	emit(eventName, ...payload) {
		_verifyType(eventName)

		const handles = this.eventBus[eventName]
		if (!handles || !handles.length) return

		handles.forEach(handle => {
			handle.callback.apply(handle.ctx, payload)
		})
	}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/296633.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

[SwiftUI]工程最低适配iOS13

问题&#xff1a; 新建工程&#xff0c;选择最低支持iOS13报错&#xff1a; main() is only available in iOS 14.0 or newer Scene is only available in iOS 14.0 or newer WindowGroup is only available in iOS 14.0 or newer 解决&#xff1a; 注释掉上面代码&#x…

短剧分销系统搭建:其成为普通人创业的新选择?短剧的红利有多高?

去年&#xff0c;短剧进入到了爆发期&#xff0c;成为了年轻人闲暇时间的娱乐方式。短剧每集只有几分钟时间&#xff0c;非常适合当下大众的碎片化时间。根据当下短剧的发展趋势&#xff0c;短剧的市场规模将逐渐赶超电影票房。 目前短剧还进行了多元化发展&#xff0c;逐渐走…

两数之和 ? 三数之和? 四数之和? 统统搞定

&#x1f388;个人主页:&#x1f388; :✨✨✨初阶牛✨✨✨ &#x1f43b;推荐专栏1: &#x1f354;&#x1f35f;&#x1f32f;C语言初阶 &#x1f43b;推荐专栏2: &#x1f354;&#x1f35f;&#x1f32f;C语言进阶 &#x1f511;个人信条: &#x1f335;知行合一 前言 声明…

高性价比LDR6028Type-C转3.5mm音频和PD快充转接器

随着市面上的大部分手机逐渐取消了3.5mm音频耳机接口&#xff0c;仅保留一个Type-C接口&#xff0c;追求音质和零延迟的用户面临着一大痛点。对于这些用户&#xff0c;Type-C转3.5mm接口线的出现无疑是一大福音。这款线材在刚推出时就受到了手机配件市场的热烈欢迎&#xff0c;…

Python武器库开发-武器库篇之子域名扫描器开发(四十一)

Python武器库开发-武器库篇之子域名扫描器开发(四十一) 在我们做红队攻防或者渗透测试的过程中&#xff0c;信息收集往往都是第一步的&#xff0c;有人说&#xff1a;渗透的本质就是信息收集&#xff0c;前期好的信息收集很大程度上决定了渗透的质量和攻击面&#xff0c;本文将…

AI数字人国内人工智能产业发展趋势

随着科技的不断进步&#xff0c;人工智能&#xff08;Artificial Intelligence&#xff0c;简称AI&#xff09;已成为当今社会的热门话题。作为一种复杂而高级的技术&#xff0c;人工智能在国内发展势头迅猛。本文将探讨AI数字人国内人工智能产业的发展趋势。 首先&#xff0c…

科锐16位汇编学习笔记01汇编基础和debug使用

为什么学习16位汇编&#xff1f; 16位操作指令最多能够操作两个字节&#xff0c;且更能够体现出与硬件的交互。16位下的指令和32位汇编的指令差不多。16位汇编的指令在32位一样使用.要学好汇编必须要了解一点点硬件知识,16汇编是直接操作硬件,32位汇编指令跟硬件隔离了 硬件运…

simulink代码生成(六)——中断向量模块的配置

假如系统中存在多个中断&#xff0c;需要合理的配置中断的优先级与中断向量表&#xff1b;在代码生成中&#xff0c;要与中断向量表对应&#xff1b;中断相关的知识参照博客&#xff1a; DSP28335学习——中断向量表的初始化_中断向量表什么时候初始化-CSDN博客 F28335中断系…

目标检测-One Stage-YOLOv2

文章目录 前言一、YOLOv2的网络结构和流程二、YOLOv2的创新点预处理网络结构训练 总结 前言 根据前文目标检测-One Stage-YOLOv1可以看出YOLOv1的主要缺点是&#xff1a; 和Fast-CNN相比&#xff0c;速度快&#xff0c;但精度下降。&#xff08;边框回归不加限制&#xff09;…

24年初级会计资格考试报名信息采集流程共10大步骤,千万不要搞错

2024年初级会计资格考试报名信息采集流程共10大步骤&#xff0c;不要搞错哦&#xff1b; 第一步&#xff1a;输入证件号、点击登录 第二步&#xff1a;阅读采集须知 第三步&#xff1a;填写个人信息&#xff08;支付宝搜索"亿鸣证件照"或者微信搜索"随时照&q…

uniapp 日历组件

我们的需求是显示当前月和下个月的排班表 引入 uniapp 日历组件 uni-calendar 做法有两种&#xff0c;一种是直接去修改组件&#xff0c;还有就是文档中提供的 selected 方法 修改组件的就不写了 <uni-calendar :lunar"true" :selected"selected" :in…

2023 hnust 湖南科技大学 大四上 计算机图形图像技术 课程 期末考试 复习资料

计算机图形图像技术复习资料 前言 改编自&#xff1a;https://blog.csdn.net/Liu_Xin233/article/details/135232531★重点&#xff0c;※补充github 考试题型 简述题&#xff08;10分4题&#xff0c;共40分&#xff09; 第1章的基本内容三维观察流水线中的基本概念与理解三…

使用Python给图片加水印(通过OpenCV和Pillow实现,内含完整代码链接)

from PIL import Image, ImageFont, ImageDraw, ImageEnhance, ImageChops import cv2 import math import numpy as npdef crop_image(im):"""裁剪图片边缘空白"""bg Image.new(mode"RGBA", sizeim.size)bbox ImageChops.differenc…

ASPICE4.0标准参考模型

aspice4.0 已经发布了&#xff0c;最近正在规划公司开发流程向4.0升级&#xff0c;研究对比了下4.0和3.1的改变&#xff0c;整体来说4.0减少了很多3.1不实用的过程&#xff0c;增加了硬件过程&#xff0c;机器学习过程&#xff0c;过程细节叶更加贴近项目的实际需求&#xff0c…

【Python特征工程系列】教你利用逻辑回归模型分析特征重要性(源码)

这是Python特征工程系列原创文章&#xff0c;我的第191篇原创文章。 一、问题 应用背景介绍&#xff1a; 如果有一个包含数十个甚至数百个特征的数据集&#xff0c;每个特征都可能对你的机器学习模型的性能有所贡献。但是并不是所有的特征都是一样的。有些可能是冗余的…

【年终总结系列 2023】新起点,同时追寻更高的起点

什么是攀登者&#xff0c;用一个场景来概括就是&#xff1a;经常弯腰低头手脚并用向上攀爬&#xff0c;待到山的顶峰后终于可以舒展一下身体&#xff0c;但若舒展的时间过长便会觉得无聊&#xff0c;此时向远处眺望&#xff0c;发现了更高的山峰&#xff0c;便又充满了激情。对…

上门洗车小程序开发源码,预约上门或到店洗车

预约上门洗车小程序&#xff0c;可以预约上门服务&#xff0c;也可以预约到店洗车&#xff0c;可以在线开通会员&#xff0c;领优惠券&#xff0c;分销推广。门店商家端可以管理订单&#xff0c;查看收益。 该系统分为三个端&#xff1a;用户端、商家端、管理后台。 一 用户端…

数据结构 模拟实现Stack栈(数组模拟)

目录 一、栈的概念 二、栈的接口 三、栈的方法实现 &#xff08;1&#xff09;push方法 &#xff08;2&#xff09;pop方法 &#xff08;3&#xff09;peek方法 &#xff08;4&#xff09;size方法 ​编辑 &#xff08;5&#xff09;empty方法 四、最终代码 一、栈的…

我们公司内应届生身上的6个共性问题

如题目&#xff0c;本文主要是根据我们公司内真实的应届生身上共同的问题&#xff0c;总结而来。 1. 一天会做很多工作&#xff1a;会跟很多人对接&#xff0c;会一会忙这个一会忙哪个 现象&#xff1a; 说实话&#xff0c;这种情况&#xff0c;我看着都替她着急。自己正在解…

【2058错误】sql软件链接数据库 mysql 报错误2058

【2058错误】sql软件链接数据库报错误2058 操作&#xff1a;仅需在mysql登陆之后运行一行代码即可&#xff1a;注意1.后面必须是%&#xff0c;而不是别人说的 localhost2.此处的password是你自己的mysql密码。 操作&#xff1a;仅需在mysql登陆之后运行一行代码即可&#xff1a…