背景
以下是手写系列内容预告,初步计划一周一个主题。
- 手写Promise、Promise.all、Promise.race
- 手写发布订阅模式
- 手写简易Redux
- 手写简易模块加载器
- 手写简易模块打包器
- 手写简易React
- 手写简易React Hooks
- ......(根据小伙伴的提议待定)
手写观察者/发布订阅模式
Subject是构造函数,new Subject() 创建一个主题对象,该对象内部维护订阅当前主题的观察者数组。主题对象上有一些方法,如添加观察者(addObserver)、删除观察者(removeObserver)、通知观察者更新(notify)。 当notify 时实际上调用全部观察者 observer 自身的 update 方法。
Observer 是构造函数,new Observer() 创建一个观察者对象,该对象有一个 update 方法,观察者可以订阅主题,实际上是把自己加入到主题的订阅者列表里。
class Subject {
observers = []
addObserver(observer) {
this.observers.push(observer)
}
removeObserver(observer) {
let index = this.observers.indexOf(observer)
if(index > -1){
this.observers.splice(index, 1)
}
}
notify() {
this.observers.forEach(observer => {
observer.update()
})
}
}
class Observer{
update() {}
subscribeTo(subject) {
subject.addObserver(this)
}
}
测试代码
let subject = new Subject()
let observer = new Observer()
observer.update = function() {
console.log('observer update')
}
observer.subscribeTo(subject) //观察者订阅主题
subject.notify()
手写一个EventBus
观察者模式和发布订阅模式具体的写法和区别没有官方的定义,下面这种写法反而更常见。
class EventBus {
map = {}
on(type, handler) {
this.map[type] = (this.map[type] || []).concat(handler)
}
fire(type, data) {
this.map[type] && this.map[type].forEach(handler => handler(data))
}
off(type, handler) {
if(this.map[type]) {
if(!handler) {
delete this.map[type]
} else {
let index = this.map[type].indexOf(handler)
this.map[type].splice(index, 1)
}
}
}
}
测试代码
const eventBus = new EventBus()
eventBus.on('click:btn', data => {
console.log(data)
})
eventBus.fire('click:btn', {a: 1, b: 2})
eventBus.off('click:btn')
eventBus.fire('click:btn', {a: 1, b: 2})
如果觉得有用,点个赞让作者开心一下呗~