发布-订阅
模式也是经典的设计模式
之一,它在前端很多地方都有应用,比如javascript事件池
,Vue的$on、$off
,nodejs的events模块和socket通信
等等都有应用,也是前端面试比较火热的考点之一,接下来给大家详细介绍下发布-订阅模式
发布-订阅模式定义了对象间的一种一对多的依赖关系
,当一个对象的状态发生变化时,所有依赖它的对象都将得到通知
。在JavaScript开发中,我们一般用事件模型来替代传统的发布-订阅模式
要手写一个简单的发布订阅模式,其实现思路如下:
- 先初始化一个
events对象
- 调用
on
方法时,将事件名称eventName
和监听函数fn
存入events对象
中 - 调用
emit
方法时,通过事件名称eventName
从events对象
中取出对应的回调并执行 off
方法:通过事件名称eventName
找出events对象
对应的监听函数并清除once
方法:被emit
触发一次后就立即调用off
方法移除监听,也就是调用once
传入的监听函数只会执行一次
代码不多,所以直接上完整代码
class EventEmitter {constructor() {this.events = {}}on(eventName, fn) {if (!this.events[eventName]) {this.events[eventName] = []}this.events[eventName].push(fn)return this}once(eventName, fn) {const func = (...args) => {this.off(eventName, func)fn.apply(this, args)}this.on(eventName, func)return this}emit(eventName, ...args) {if (!this.events[eventName]) return thisthis.events[eventName].forEach(fn => {fn.apply(this, args)});return this}off(eventName, fn) {if (!this.events[eventName]) return thisif (typeof fn === 'function') {this.events[eventName] = this.events[eventName].filter((f) => f !== fn)return this}this.events[eventName] = nullreturn this}
}
const events = new EventEmitter();events.on('event1', () => {console.log('event1', '第一个监听函数')
})
events.on('event1', () => {console.log('event1', '第二个监听函数')
})
events.emit('event1')const fn1 = () => {console.log('event2', '第一个监听函数')
}
const fn2 = () => {console.log('event2', '第二个监听函数')
}
events.on('event2', fn1)
events.on('event2', fn2)
events.off('event2', fn1);// 打印结果:
// event1 第一个监听函数
// event1 第二个监听函数// 上面代码有疑问请阅读下面代码,这部分解答了上面读者的疑问
const events = new EventEmitter();// event1部分
events.on('event1', () => {console.log('event1', '第一个监听函数')
})
events.on('event1', () => {console.log('event1', '第二个监听函数')
})
events.emit('event1')// event2部分
const fn1 = () => {console.log('event2', '第一个监听函数')
}
const fn2 = () => {console.log('event2', '第二个监听函数')
}
events.on('event2', fn1)
events.off('event2', fn1);
events.on('event2', fn2)
events.emit('event2')
- 可以广泛应用于异步编程中,这是一种替代传递回调函数的方案;
- 可以取代对象之间硬编码的通知机制,一个对象不用再显示地调用一个对象的接口