带你认识JS中的观察者模式
这是我参与2022首次更文挑战的第21天,活动详情查看:2022首次更文挑战」。
导语
观察者模式是开发中常用的设计模式之一,顾名思义就是观察者和被观察者之间存在的依赖关系和交互所产生的设计模式。在实际开发中,观察者模式被应用到各个场景,诸多的常用框架,API都是基于观察者模式去实现的,本文将探讨什么是观察者模式,如何实现观察者模式以及观察者模式在开发中的应用。
什么是观察者模式
观察者模式:定义对象间一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并自动更新。此时被改变状态的对象我们称之为被观察者,被通知到的对象我们称为观察者。
简单的观察者模式:
<!DOCTYPE html>
<html>
</script>
<body style="padding: 10px">
<div>
<button onclick="onclick">click me</button>
</div>
</body>
</html>
<script>
onclick = () => {
alert('click')
}
</script>
对于 button, onclick
为事件,在 script 中监听了 button 的 onclick 事件,此处button为被观察者,当 button 的点击状态发生改变时,会通过事件的形式通知到观察者,这就是一个简单的观察者模式。
观察者模式可以实现多个模块之间的解藕,在开发中可以让观察者更方便地捕获被观察者的状态变更并作出处理。
实现一个观察者模式
假设有一个 class weather
,表示天气的一个类,当我们天气变化时,要提醒人们带伞。在这里,天气变化是一个不确定的触发时机,那么最好的方式就是通过观察者模式去实现,核心逻辑就是给 weather 实现一个观察者的设计,当 weather 的某个状态发生改变时,通知观察者。
观察者模式的核心
1. 实现对象
要想实现一个观察者模式,首先考虑在哪里实现。观察者模式是被观察者一对多个观察者,所以在被观察者中去实现是比较合理的。在这里,我们可以选择在 Weather
class 中去实现。
2. 核心内容
观察者模式的核心内容: 订阅,发布,删除
订阅: 为 被观察者添加事件,以及事件触发时观察者所要执行的回调函数。
发布: 当事件被触发时,发起通知,通知观察者去执行订阅时添加的回调函数。
删除: 当观察者不在需要观察被观察者状态时,避免内存占用,应当清除事件。
3.设计 Weather class
class Weather {
constructor() {
this.content = 'sunny' // 存放天气状态
this.callbackMap = new Map() // 存放观察者回调
}
}
4. 订阅
// 订阅
on(eventType, callback) {
if (!this.callbackMap.has(eventType)) {
// 当前事件不存在,重新添加事件的观察者数组
this.callbackMap.set(eventType, [])
}
if (typeof callback === 'function') {
// 一个事件可以是多个观察者,所以应当设计为数组。 回调函数添加到对应事件数组中
this.callbackMap.get(eventType).push(callback)
} else {
console.log('缺少 callback')
}
return this
}
5. 发布
// 发布
emit(eventType, ...args) {
if (this.callbackMap.has(eventType)) { // 获取订阅事件所对应的回调数组
const subList = this.callbackMap.get(eventType)
subList.forEach(fun => {
// 执行回调
fun.apply(null, args)
});
} else {
console.log('发布:事件没有注册')
}
return this
}
6. 删除
// 删除
off(eventType) {
if (this.callbackMap.has(eventType)) {
this.callbackMap.delete(eventType)
} else {
console.log('删除:事件没有注册')
}
}
7. 天气改变时发布通知
changeWeather(content) {
if (this.content !== content) {
this.content = content
this.emit('weatherChange', this.content)
}
}
8. 完整代码
class Weather {
constructor() {
this.content = 'sunny' // 存放天气状态
this.callbackMap = new Map() // 存放观察者回调
}
// 订阅
on(eventType, callback) {
if (!this.callbackMap.has(eventType)) {
// 当前事件不存在,重新添加事件的观察者数组
this.callbackMap.set(eventType, [])
}
if (typeof callback === 'function') {
// 一个事件可以是多个观察者,所以应当设计为数组。 回调函数添加到对应事件数组中
this.callbackMap.get(eventType).push(callback)
} else {
console.log('缺少 callback')
}
return this
}
// 发布
emit(eventType, ...args) {
if (this.callbackMap.has(eventType)) {
const subList = this.callbackMap.get(eventType)
subList.forEach(fun => {
fun.apply(null, args)
});
} else {
console.log('发布:事件没有注册')
}
return this
}
// 删除
off(eventType) {
if (this.callbackMap.has(eventType)) {
this.callbackMap.delete(eventType)
} else {
console.log('删除:事件没有注册')
}
}
changeWeather(content) {
if (this.content !== content) {
this.content = content
this.emit('weatherChange', this.content)
}
}
}
9. 测试
const weather = new Weather()
weather.on('weatherChange', (content) => {
console.log('weather change to ' + content)
})
weather.changeWeather('rain')
weather.off('weatherChange')
weather.changeWeather('snow')
结果:
weather change to rain
发布:事件没有注册
至此完整实现了观察订阅模式的一个小案例。举一反三,相信你也是可以通过观察者模式去实现一个 Promise 的。