最近在试读vue的源码(vue3都要出来了,vue2的源码还没去读,惭愧惭愧),发现自己对观察者以及发布者-订阅者模式的理解不够,立文来梳理记录一下自己的思路。
一、观察者模式和发布者-订阅者模式模式的区别
有些人认为观察者模式 === 发布者-订阅者模式,也有人觉得两者存在一些区别,其实两者的确有一些区别,但是两者的目的都是为了改善传统的通知机制,解耦通知方和被通知方的关系。那为什么发布者-订阅者模式存在区别呢?其实是因为后者相对于前者而言,多了一个类似于调度中心的存在,后者能集中管理多个主题对象,并且观察者和发布者之间是不存在直接关系的,在调度中心可以很方便的实现分发管理,这个特性让发布者-订阅者模式变得更加灵活。
二、观察者和发布者-订阅者模式的简单实现
观察者模式
class Observer {
constructor(label) {
this.label = label
}
update() {
console.log(`update${this.label}`)
}
}
class Target {
constructor() {
this.observerList = []
}
add(observer) {
this.observerList.push(observer)
}
remove(observer) {
this.observerList = this.observerList.filter(ob => ob !== observer)
}
notify() {
this.observerList.forEach(ob => {
ob.update()
})
}
}
let tar = new Target()
for (let i = 0; i < 10; i++) {
tar.add(new Observer(i))
}
tar.notify()//打印update1到update10
复制代码
发布者-订阅者模式
class Publisher {
constructor() {
this.subscribers = {}
this.cache = {}
}
add(type, fn) {
if (Object.prototype.toString.call(fn) !== '[object Function]') return
this.subscribers[type] ? this.subscribers[type].push(fn) : this.subscribers[type] = [fn]
return this
}
publish(type, theme) {
//存储主题信息
this.cache[type] ? this.cache[type].push(theme) : this.cache[type] = [theme]
let subscribers = this.subscribers[type]
if (!subscribers || !subscribers.length) {
console.warn('no subscriber in subscribers.')
return this
}
subscribers.forEach(fn => fn.call(this, theme))
return this
}
unbind(type, fn) {
this.subscribers[type] = this.subscribers[type].filter(item => item !== fn)
return this
}
history(type, fn) {
let cache = this.cache[type] || []
cache.forEach(item => fn.call(this, item))
return this
}
}
let sub1 = data => {
console.log(`sub1${data}`)
}
let sub2 = data => {
console.log(`sub2${data}`)
}
let sub3 = data => {
console.log(`sub3${data}`)
}
let publisher = new Publisher()
publisher.add('click', sub1).add('click', sub2).add('change', sub3)
publisher.publish('click', '第一次click').publish('change', '第一次change')
publisher.unbind('click', sub1).publish('click', '第二次点击').history('click', data => {
console.log('history' + data)
})
打印结果:
/***
sub1第一次click
sub2第一次click
sub3第一次change
sub2第二次点击
history第一次click
history第二次点击
***/
复制代码
如以上代码,发布者订阅者模式可以在调度中心(相当于代码中的publish函数)进行一些定制化的操作,比如进行节流操作,过滤操作,权限控制等等,个人感觉会比较灵活一点。
先写这么多~有不对的欢迎指正~