前言
发布-订阅
模式也是经典的设计模式
之一,它在前端很多地方都有应用,比如javascript事件池
,Vue的$on、$off
,nodejs的events模块和socket通信
等等都有应用,也是前端面试比较火热的考点之一,接下来给大家详细介绍下发布-订阅模式
。
发布-订阅模式定义
发布-订阅模式定义了对象间的一种一对多的依赖关系
,当一个对象的状态发生变化时,所有依赖它的对象都将得到通知
。在JavaScript开发中,我们一般用事件模型来替代传统的发布-订阅模式。
发布-订阅模式实现
要手写一个简单的发布订阅模式,其实现思路如下:
- 先初始化一个
events对象
- 调用
on
方法时,将事件名称eventName
和监听函数fn
存入events对象
中 - 调用
emit
方法时,通过事件名称eventName
从events对象
中取出对应的回调并执行 off
方法:通过事件名称eventName
找出events对象
对应的监听函数并清除once
方法:被emit
触发一次后就立即调用off
方法移除监听,也就是调用once
传入的监听函数只会执行一次
代码不多,所以直接上完整代码。
js
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 this
this.events[eventName].forEach(fn => {
fn.apply(this, args)
});
return this
}
off(eventName, fn) {
if (!this.events[eventName]) return this
if (typeof fn === 'function') {
this.events[eventName] = this.events[eventName].filter((f) => f !== fn)
return this
}
this.events[eventName] = null
return this
}
}
简单测试一下:
js
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 第二个监听函数
ok,大功告成了!
发布-订阅模式的作用
- 可以广泛应用于异步编程中,这是一种替代传递回调函数的方案;
- 可以取代对象之间硬编码的通知机制,一个对象不用再显示地调用一个对象的接口。
发布-订阅模式的优缺点
优点:
- 解耦性:解耦了发布者和订阅者,让彼此独立,更加容易维护和扩展;
- 异步通信:发布者和订阅者不受时间限制,可以在任意时间发布和订阅事件。
缺点:
- 难以调试和追踪 :如果过度使用的话,系统中存在大量
发布者和订阅者
,会导致程序难以跟踪维护和理解。比如实际vue项目
中大量运用了事件总线$bus
的通信,会增加追踪和调试程序的难度
。 - 增加系统复杂性:引入发布订阅模式到系统中进行消息通信,无疑会增加系统复杂度。
小结
发布-订阅模式
在各个框架源码中有非常多的应用,我们了解其内部原理和实现思路之后,其实也能帮助我们更好地去理解和使用它,同时这个也是前端面试的必备知识点之一
,掌握好它准没错!