前端面试题:如何实现事件总线 Event Bus
介绍
通常作为多个模块间的通信机制,相当于一个事件管理中心,一个模块发送消息,其它模块接受消息,就达到了通信的作用。
原理
本质上是采用了发布-订阅的设计模式,比如多个模块 A、B、C 订阅了一个事件 EventX,然后某一个模块 X 在事件总线发布了这个事件,那么事件总线会负责通知所有订阅者 A、B、C,它们都能收到这个通知消息,同时还可以传递参数。
分析
如何使用 JavaScript 来实现一个简单版本的 Event Bus
- 创建一个类
- 又一个事件池,用来保存发布的事件
- 有一个发布的方法,将事件发布
- 有一个订阅的监听机制,来触发事件的回调
- 有取消订阅的机制
- 有只订阅一次的机制
实现
创建一个类
class EventBus {}
创建一个事件池
class EventBus {
private events: {
[key: string]: Array<{fn: Function, isOnce: boolean}>
}
constructor() {
this.events = {}
}
}
发布事件
- 第一个参数是事件的KEY值, 剩余是接收事件的参数
- 在处理事件池中的监听时,将只监听一次的事件去除
class EventBus {
private events: {
[key: string]: Array<{fn: Function, isOnce: boolean}>
}
constructor() {
this.events = {}
}
emit(type: string, ...args: any[]) {
const fnList = this.events[type]
if (fnList == null) return
this.events[type] = fnList.filter(item => {
const {fn, isOnce} = item
fn(...args)
return !isOnce
})
}
}
订阅事件
class EventBus {
private events: {
[key: string]: Array<{fn: Function, isOnce: boolean}>
}
constructor() {
this.events = {}
}
on(type: string, fn: Function, isOnce:boolean=false){
const events = this.events
if (events[type] == null) {
this.events[type] = []
}
this.events[type].push({fn: fn, isOnce})
}
emit(type: string, ...args: any[]) {
const fnList = this.events[type]
if (fnList == null) return
this.events[type] = fnList.filter(item => {
const {fn, isOnce} = item
fn(...args)
return !isOnce
})
}
}
只订阅一次
class EventBus {
private events: {
[key: string]: Array<{fn: Function, isOnce: boolean}>
}
constructor() {
this.events = {}
}
on(type: string, fn: Function, isOnce:boolean=false){
const events = this.events
if (events[type] == null) {
this.events[type] = []
}
this.events[type].push({fn: fn, isOnce})
}
once(type: string, fn: Function) {
this.on(type, fn, true)
}
emit(type: string, ...args: any[]) {
const fnList = this.events[type]
if (fnList == null) return
this.events[type] = fnList.filter(item => {
const {fn, isOnce} = item
fn(...args)
return !isOnce
})
}
}
取消订阅
class EventBus {
private events: {
[key: string]: Array<{fn: Function, isOnce: boolean}>
}
constructor() {
this.events = {}
}
on(type: string, fn: Function, isOnce:boolean=false){
const events = this.events
if (events[type] == null) {
this.events[type] = []
}
this.events[type].push({fn: fn, isOnce})
}
once(type: string, fn: Function) {
this.on(type, fn, true)
}
emit(type: string, ...args: any[]) {
const fnList = this.events[type]
if (fnList == null) return
this.events[type] = fnList.filter(item => {
const {fn, isOnce} = item
fn(...args)
return !isOnce
})
}
off(type: string, fn: Function) {
this.events[type] = this.events[type].filter(item => {
return item.fn !== fn
})
}
}
取消订阅某个事件
class EventBus {
private events: {
[key: string]: Array<{fn: Function, isOnce: boolean}>
}
constructor() {
this.events = {}
}
on(type: string, fn: Function, isOnce:boolean=false){
const events = this.events
if (events[type] == null) {
this.events[type] = []
}
this.events[type].push({fn: fn, isOnce})
}
once(type: string, fn: Function) {
this.on(type, fn, true)
}
emit(type: string, ...args: any[]) {
const fnList = this.events[type]
if (fnList == null) return
this.events[type] = fnList.filter(item => {
const {fn, isOnce} = item
fn(...args)
return !isOnce
})
}
off(type: string, fn: Function) {
// 如果没传 函数就是解除所有
if (!fn) {
this.events[type] = []
}else {
this.events[type] = this.events[type].filter(item => {
return item.fn !== fn
})
}
}
}
单例模式应用
在上层实例中单例
将事件总线引入到上层实例使用,只需要保证在一个上层实例中只有一个 EventBus,如果上层实例有多个,意味着有多个事件总线,但是每个上层实例管控自己的事件总线。
首先在上层实例中建立一个变量用来存储事件总线,只在第一次使用时初始化,后续其他模块使用事件总线时直接取得这个事件总线实例。
// 上层实例
class LWebApp {
private _eventBus?: EventBus;
constructor() {}
public getEventBus() {
// 第一次初始化
if (this._eventBus == undefined) {
this._eventBus = new EventBus();
}
// 后续每次直接取唯一一个实例,保持在LWebApp实例中单例
return this._eventBus;
}
}
// 使用
const eventBus = new LWebApp().getEventBus();
在全局中单例
有时候我们希望不管哪一个模块想使用我们的事件总线,我们都想这些模块使用的是同一个实例,这就是全局单例,这种设计能更容易统一管理事件。
写法同上面的类似,区别是要把 _eventBus 和 getEventBus 转为静态属性。使用时无需实例化 EventBusTool 工具类,直接使用静态方法就行了。
// 上层实例
class EventBusTool {
private static _eventBus?: EventBus;
constructor() {}
public static getEventBus(): EventBus {
// 第一次初始化
if (this._eventBus == undefined) {
this._eventBus = new EventBus();
}
// 后续每次直接取唯一一个实例,保持全局单例
return this._eventBus;
}
}
// 使用
const eventBus = EventBusTool.getEventBus();
2周刷完100道前端优质面试真题,双越老师力作
链接:https://pan.baidu.com/s/1Fr4TUipNKGyaZxssKIkc3w
提取码:ylsp
也欢迎使用我的小程序,惊喜不断!
标签:面试题,const,string,Bus,events,type,Event,fn,isOnce From: https://www.cnblogs.com/buxiugangzi/p/17116955.html