js实现自定义事件
自定义事件类自身属性eventMapon方法off方法emit方法onOnce方法自定义事件类
从自定义事件的功能上分析,它应该拥有以下几个方法:
能够注册某个事件,并允许添加相应的方法能够移除某个事件能够触发某个事件有一个队列或者映射来缓存当前已注册的事件
话不多说,先上代码
class Observer {constructor() {this.eventMap = {}}/*** 注册事件* @param {String} event 事件名称 * @param {Function} fn 回调函数 */on(event, fn) {const map = this.eventMapif (!map[event]) {map[event] = []}map[event].push(fn)}/*** 触发事件*/emit(event, ...args) {const map = this.eventMapif (map[event].length) {map[event].forEach(fn => {fn.apply(null, args)});} else {console.error('无待执行函数')}}/*** 移除事件*/off(event, fn) {const map = this.eventMapconst index = map[event]?.indexOf(fn)if (index > -1) {map[event].splice(index, 1)} else {console.error('目标函数不存在')}}/*** 注册只执行一次的事件*/onOnce(event, fn) {const self = thisfunction on() {fn.apply(null, arguments)// 先执行fnself.off(event, on)// 立刻移除fn}this.on(event, on)// 注册代理函数}}
自身属性eventMap
Observer 实例化以后用一个eventMap对象
保存所有事件,事件的值是一个数组
,存储将要被执行的函数
// 例:eventMap = {input:[FunctionA],change:[FunctionA,FunctionB]}
on方法
用来注册事件
on(event, fn) {const map = this.eventMapif (!map[event]) {map[event] = []}map[event].push(fn)}
传入参数为:事件名称
以及回调函数
。
首先查看当前map中是否已注册该事件,没有注册就先初始化为一个空数组,随后把回调push进去。由于是数组结构,也就是说允许多次注册
同一个事件,每次注册在数组中多放入一个回调
const o= new Observer()o.on('change',()=>{console.log('我第一次注册')});o.on('change',()=>{console.log('我又注册了一次')});
off方法
既然可以注册事件,就必然需要有移除事件的操作
off(event, fn) {const map = this.eventMapconst index = map[event]?.indexOf(fn)if (index > -1) {map[event].splice(index, 1)} else {console.error('目标函数不存在')}}
传入的参数为事件名称
以及上次的回调函数
。
获取当前事件对应的函数数组,查找目标函数的索引位置,如果这个函数被找到了,就把他剔除出去。数组中其他函数不受影响。
emit方法
事件的触发很简单,只需要调用emit方法,传入事件名称就可以了。当然允许传入需要的若干个参数。
emit(event, ...args) {const map = this.eventMapif (map[event].length) {map[event].forEach(fn => {fn.apply(null, args)});} else {console.error('无待执行函数')}}
首先查看目标事件是否存在,若数组不为空,就遍历调用其中的函数,并顺便把emit传入的参数传递过去
onOnce方法
如果我想事件只能执行一次,该如何处理呢?
onOnce(event, fn) {const self = thisfunction on() {fn.apply(null, arguments)// 先执行fnself.off(event, on)// 立刻移除fn}this.on(event, on)// 注册代理函数}
这就要求在事件注册的时候做一些处理,就是把回调函数替换成我们定义的on函数,它干了两件事情:
先执行一下原本的fn执行之后立刻移除原事件,这样事件就只能执行一次。
const o= new Observer()o.onOnce('change', (name, age) => {console.log(`我是${name},现在已经${age}岁了`)})o.emit('change', '熏悟空', 600)// 第一次会执行------->输出“我是熏悟空,现在已经600岁了”o.emit('change', '居八戒', 400)// 第二次事件已经被移除,不会执行