发布订阅模式与简单实现
本文写于 2020 年 9 月 22 日
观察者模式(Observer Pattern)是一种设计模式,也可以叫做「发布-订阅模式」。
等等,其实我也不清楚,通常你在网上会看到上面这一段话,但我在 Angular 文档中读到谷歌的说法是这样的:观察者模式和发布/订阅模式非常相似(但不完全一样)。
好吧,我们姑且就直说发布订阅模式吧。
发布订阅模式就像你订了一本杂志,每当杂志新刊发布的时候,所有订阅过杂志的人都会收到新刊一样。
观察者模式定义了对象之间一种 一对多 的依赖关系,每当该对象的状态发生改变的时候,所依赖于它的其他对象都将得到通知。
在 JavaScript 中,我们会经常接触到一个很类似的东西——事件。
事件与观察者模式
我们先为一个 #app 元素添加两个点击事件:
document.querySelector('#app').addEventListener('click', () => {
// 事件 1
});
document.querySelector('#app').addEventListener('click', () => {
// 事件 2
});
这个时候,我们点击 #app 元素或者使用 document.querySelector('#app').click()
或者 document.querySelector('#app').dispatchEvent('click')
来触发时,这两个事件都会被触发——是不是类似与很多对象依赖于一个对象,该对象改变后所有依赖者都会被触发。
自定义「发布-订阅」
我们来自定义一个事件来试试。
首先我们需要一个数组储存所有的触发函数,这样我们就可以对其进行遍历,后触发每一个事件。
const children = [];
我们还需要一个方法,来对 children 数组进行 push
。
const listen = (fn) => {
children.push(fn);
}
我们再增加一个触发函数 trigger
,让它去遍历并触发我们的 children
const trigger = () => {
children.forEach((child) => {
child();
})
}
OK,接下来我们整合一下:
class eventHub {
children = {}
listen(key, fn) {
if(this.children[key]) {
this.children[key].push(fn);
} else {
this.children[key] = [fn]
}
}
trigger(key) {
this.children[key]?.forEach((child) => {
child();
});
}
remove(key, fn) {
if(this.children[key]) {
const children =this.children[key]
const target = children.indexOf(fn)
this.children[key] = [...children.slice(0, target), ...children.slice(target + 1)]
}
}
}
这里我们将数组换成了对象,并且为函数增加了参数,因为我们希望够可以对多个事件进行监听。
还添加了一个移除事件——所以我们最好使用具名函数,如果是匿名函数就无法移除监听了!
有哪些例子?
例如一个电商网站的登陆系统,需要获取用户基础信息后,获取用户的各项历史记录:
function onLogin() {
getUserInfo()
.then(() => {
return getUserHistory()
})
.then(() => {
getUserXXX()
})
}
那么一旦我们需要新增一个功能,就需要在这个长函数中增加新项。
如果使用了发布-订阅模式,就可以非常简单的写上新的、独立的事件监听了。
(完)