在秋招刚开始的时候,我信心十足的投递了字节的前端岗位,也不出意料约到了面试,因为是秋招第一个面试,也不了解字节还有面评这个说法,第一次就失败了,还脏了面评😭,当时让我手写的就是发布订阅模式,没有写出来,今天突然想起,就在写一下吧。
我会以小白的视角尽可能写的详细一点,该发布订阅模式包含三个功能,分别是订阅事件(subscribe),取消订阅事件(unSubscribe)和发布事件(dispatch),希望有误的地方大家可以指出。
// 定义一个class类
class EventCenter {
// 定义事件容器,用来装事件数组
constructor () {
this.events = {}
}
// 订阅事件
subscribe (eventsName, callback) {
// 判断当前容器是否有这个事件
if (!this.events[eventName]) {
// 如果没有的话,创建这个事件的容器
this.events[eventName] = []
}
// 如果有的话,在这个容器中存放事件callback
this.events[eventName].push(callback)
}
// 取消订阅事件
unSubscribe (eventName, callback) {
// 先判断是否有这个事件
if (!this.events[eventName]) {
// 没有的话抛出error
return new Error('do not have this event')
}
// 有事件的话,判断是否有事件callback
if (!callback) {
// 没有事件callback的话,直接移除这个事件
delete this.events[eventName]
} else {
// 有事件callback的话,找到其索引,未找到其索引的话返回 -1
const index = this.events[eventName].findIndex((el) => el === callback)
// 判断是否有这个索引
if (index === -1) {
// 未找到索引,抛出error
return new Error('do not have this event callback')
}
// 找到其索引,移除这个事件的callback
this.events[eventName].splice(index, 1)
// 移除之后判断事件容器是否为0,如果为0,就代表没有任何订阅者了,删除该事件
if(this.events.length === 0) {
delete this.events[eventName]
}
}
}
// 触发事件
dispatch(eventName, ...args) {
// 判断是否有这个事件,没有的话抛出错误
if (!this.events[eventName]) {
return new Error('do not have this event')
}
// 如果有的话,触发该事件
this.events[eventName].forEach((el) => {
el(...args)
})
}
}
// 测试该发布订阅模式
// 创建一个实例
const eventCase = new EventCenter()
// 给这个事件订阅一个事件
eventCase.subscribe('click', (x, y) => {
console.log(x, y)
})
// 触发该事件
eventCase.dispatch('click', 111, 222) // 111, 222