前言
发布-订阅模式作为设计模式中的一种,在面试中是一个经常被问到的问题。在本篇文章中,作者将带领大家了解发布订阅模式,和逐步完成发布-订阅模式的函数封装
发布-订阅模式是什么
发布-订阅模式是一种消息传递模式,发布者发送消息时不会直接发送给订阅者,而是发送到一个中转站中,所有订阅了该中转站的订阅者都会统一接受该消息,通过使用这种模式可以使发布者和订阅者不必关系对方的存在,只需要关系中转站。
发布-订阅模式的优点有哪些
- 模块化:发布者和订阅者可以独立地增长和变化,而不影响其他部分。
- 可扩展性:向系统中添加新的订阅者很简单,只需要订阅他们感兴趣的主题即可。
- 异步处理:消息的发布和订阅是异步进行的,可以实现高效的异步处理。
发布-订阅模式的函数封装
在面试中,面试官通常会给我们下面一串代码,让我们完成发布-订阅模式的封装.
class EventEmitter {
constructor() {
}
//订阅事件
on() {
}
//订阅事件,只执行一次
once() {
}
//发布事件
emit() {
}
//取消订阅
off() {
}
}
在开始发布订阅模式的封装前,让我们先来看一个小例子明白发布-订阅模式的具体流程
<script>
//新建一个"promise"事件
let ev = new Event("promise");
//订阅"promise"事件,当这个事件执行时,调用fnB
window.addEventListener("promise", () => {
fnB();
});
function fnA() {
setTimeout(() => {
console.log("请求A完成");
//发布ev事件
window.dispatchEvent(ev);
}, 1000);
}
function fnB() {
setTimeout(() => {
console.log("请求B完成");
}, 500);
}
fnA();
</script>
在上面的例子中,我们订阅了一个"promise"事件,当"promise"事件发布时执行fnB。而"promise"事件只有在fnA执行完之后才被发布出来。这就是发布-订阅模式的流程。现在让我们来开始发布-订阅的函数封装
首先,我们需要在constructor中定义一个空对象来作为被订阅事件的容器。
constructor() {
this._event = {}
}
on() 订阅事件的实现
on(type, cb) {
//没有事件时往this._event中添加一个key为type,value为[cb]的键值对
if (!this._events[type]) {
this._events[type] = [cb];
} else {
//将cb作为值push进数组中
this._events[type].push(cb);
}
}
let ev = new EventEmitter();
const fn1 = (str) => {
console.log(str, "fn1");
};
const fn2 = (str) => {
console.log(str, "fn2");
};
const fn3 = (str) => {
console.log(str, "fn3");
};
ev.on("run", fn1);
ev.on("run", fn2);
ev.on("run", fn3);
//此时this._event为
//{ "run": [fn1,fn2,fn3] }
emit() 发布事件的实现
//为什么要用...args呢? 那是因为当我们调用emit发布事件的时候,可能不止传入一个参数
emit(type, ...args) {
if (this._events[type]) {
//遍历key为type的键值对,逐步执行value数组里面的函数
this._events[type].forEach((cb) => {
cb(...args);
});
} else {
console.log("没有这个事件");
return;
}
}
这时我们将emit和上面的on一起调用就能实现一个简单的发布订阅模式
但是这肯定还是不够的,面试官还会让我们完成once()和off()的实现
off() 取消订阅事件的实现
off(type, cb) {
if (this._events[type]) {
//利用过滤器把要取消的事件过滤掉,并重新赋值给this._events
this._events[type] = this._events[type].filter((item) => item !== cb);
} else {
console.log("没有这个事件");
}
}
once() 订阅事件,只执行一次
once(type, cb) {
const fn = (...args) => {
cb(...args);
this.off(type, fn);
};
//当emit()时 fn被触发一次,随后便通过this.off取消事件,再次emit时this._event[type]中没有fn
this.on(type, fn);
}
到了这里完整的发布-订阅模式便完成了封装,以下是完整代码,如果本篇文章对你有帮助的话就点给个免费的赞激励下作者吧.
class EventEmitter {
constructor() {
this._events = {}; //"run" : [fun]
}
//订阅事件
on(type, cb) {
if (!this._events[type]) {
this._events[type] = [cb];
} else {
this._events[type].push(cb);
}
}
//
once(type, cb) {
const fn = (...args) => {
cb(...args);
this.off(type, fn);
};
this.on(type, fn);
}
//发布事件
emit(type, ...args) {
if (this._events[type]) {
this._events[type].forEach((cb) => {
cb(...args);
});
} else {
console.log("没有这个事件");
return;
}
}
//取消订阅
off(type, cb) {
if (this._events[type]) {
this._events[type] = this._events[type].filter((item) => item !== cb);
} else {
console.log("没有这个事件");
}
}
}