实际开发中使用已经写好的就行,这里是为了让你理解事件总线核心是怎么实现的
实现
// 类EventBus -> 事件总线对象
class HYEventBus {
constructor() {
// 存储事件名和事件处理函数的对应关系
this.eventMap = {}
}
on(eventName, eventFn) {
let eventFns = this.eventMap[eventName]
if (!eventFns) {
eventFns = []
this.eventMap[eventName] = eventFns
}
eventFns.push(eventFn)
}
off(eventName, eventFn) {
let eventFns = this.eventMap[eventName]
if (!eventFns) return
for (let i = 0; i < eventFns.length; i++) {
const fn = eventFns[i]
if (fn === eventFn) {
eventFns.splice(i, 1)
break
}
}
// 如果eventFns已经清空了
if (eventFns.length === 0) {
delete this.eventMap[eventName]
}
}
emit(eventName, ...args) {
let eventFns = this.eventMap[eventName]
if (!eventFns) return
eventFns.forEach(fn => {
fn(...args)
})
}
}
代码解释
class HYEventBus {
constructor() {
// 存储事件名和事件处理函数的对应关系
this.eventMap = {}
}
}
- 这里定义了一个名为
HYEventBus的类,通过构造函数初始化了一个空对象this.eventMap,它的作用是用来存储不同事件名对应的事件处理函数数组,以事件名为键,事件处理函数数组为值,形成一种映射关系。
2. on 方法
on(eventName, eventFn) {
let eventFns = this.eventMap[eventName];
if (!eventFns) {
eventFns = [];
this.eventMap[eventName] = eventFns;
}
eventFns.push(eventFn);
}
用于订阅事件。接收一个事件名 eventName 和一个对应的事件处理函数 eventFn。先尝试从 this.eventMap 中获取该事件名对应的处理函数数组
如果不存在(也就是首次为该事件名添加订阅函数),就创建一个空数组来存放后续的处理函数。然后将传入的事件处理函数 eventFn 推送到对应的数组中,这样同一个事件名可以有多个订阅者,它们的处理函数都会被存放在相应的数组里。
3. off 方法
off(eventName, eventFn) {
let eventFns = this.eventMap[eventName];
if (!eventFns) return;
for (let i = 0; i < eventFns.length; i++) {
const fn = eventFns[i];
if (fn === eventFn) {
eventFns.splice(i, 1);
break;
}
}
// 如果eventFns已经清空了
if (eventFns.length === 0) {
delete this.eventMap[eventName];
}
}
用于取消订阅事件。它接收事件名 eventName 和要取消的具体事件处理函数 eventFn。
-
首先查找对应事件名的处理函数数组,如果存在该数组,就遍历数组去找到与传入的
eventFn相等的函数,找到后通过splice方法将其从数组中移除。 -
如果移除后该事件对应的处理函数数组为空了,说明没有任何函数再订阅这个事件了,那就从
this.eventMap中将这个事件名对应的键值对整个删除掉,以清理不再使用的记录。
4. emit 方法
emit(eventName,...args) {
let eventFns = this.eventMap[eventName];
if (!eventFns) return;
eventFns.forEach(fn => {
fn(...args);
});
}
用于触发某个事件。接收一个事件名 eventName 以及不定数量的参数(通过剩余参数语法 ...args 收集),
先查找该事件名对应的处理函数数组,如果存在,就遍历这个数组,依次调用每个处理函数,并将收集到的参数传递给这些处理函数,从而实现通知所有订阅该事件的部分去执行相应的逻辑。
测试
// 使用过程
const eventBus = new HYEventBus()
// 添加订阅操作
eventBus.on("navclick", (name, age, height) => {
console.log("navclick listener 01", name, age, height)
})
const click = () => {
console.log("navclick listener 02")
}
eventBus.on("navclick", click)
// 取消订阅操作
setTimeout(() => {
eventBus.off("navclick", click)
}, 5000);
const navBtnEl = document.querySelector(".nav-btn")
navBtnEl.onclick = function() {
console.log("自己监听到")
// 发布订阅操作
eventBus.emit("navclick", "why", 18, 1.88)
}