观察者 & 发布订阅模式 🔥
定义
当对象间存在一对多关系时,则使用观察者模式(Observer Pattern)。比如,当一个对象被修改时,则会自动通知依赖它的对象。
实现
观察者模式
js
class Subject {
constructor(name) {
this.name = name
this.observers = []
}
add(observer) {
this.observers.push(observer)
}
remove(observer) {
this.observers = this.observers.filter(item => item !== observer)
}
notify(str) {
this.observers.forEach(item => {
item.update(`${this.name} ${str}`)
})
}
}
class Observer {
constructor(name) {
this.name = name
}
update(str) {
console.log(`${this.name} 接收到 ${str}`)
}
}
const sub = new Subject('目标1')
const observer1 = new Observer('观察者1-1')
const observer2 = new Observer('观察者1-2')
sub.add(observer1)
sub.add(observer2)
sub.notify('更新了')
sub.remove(observer2)
sub.notify('删除了一个watcher')
const sub2 = new Subject('目标2')
const observer3 = new Observer('观察者2-1')
const observer4 = new Observer('观察者2-2')
sub2.add(observer3)
sub2.add(observer4)
sub2.notify('更新了')
发布订阅模式
js
class EventBus {
constructor() {
this.events = {
/**
* 'click':[fn1,fn2...],
* 'scroll':[fn3,fn4...]
*/
}
this.onceEvents = {}
}
on(eventName, fn) {
if (!this.events[eventName]) {
this.events[eventName] = []
}
this.events[eventName].push(fn)
}
once(eventName, fn) {
if (!this.onceEvents[eventName]) {
this.onceEvents[eventName] = []
}
this.onceEvents[eventName].push(fn)
}
emit(eventName, ...args) {
const targetEvents = this.events[eventName]
const targetOnceEvents = this.onceEvents[eventName]
if (!targetEvents && !targetOnceEvents) {
console.log('!!!没有订阅事件可触发')
return
}
if (targetEvents) {
targetEvents.forEach(fn => {
fn(...args)
})
}
if (targetOnceEvents) {
targetOnceEvents.forEach(fn => {
fn(...args)
})
//once 执行一次就删除
delete this.onceEvents[eventName]
}
}
off(eventName, fn) {
if (fn) {
//删除指定事件
if (this.events[eventName]) {
this.events[eventName] = this.events[eventName].filter(
curFn => curFn !== fn,
)
}
if (this.onceEvents[eventName]) {
this.onceEvents[eventName] = this.onceEvents[eventName].filter(
curFn => curFn !== fn,
)
}
} else {
//删除全部
if (this.events[eventName]) {
delete this.events[eventName]
}
if (this.onceEvents[eventName]) {
delete this.onceEvents[eventName]
}
}
}
}
const eventBus = new EventBus()
//第一种事件
const clickFn1 = res => console.log(`${res} 1`)
eventBus.on('click', clickFn1)
const clickFn2 = res => console.log(`${res} 2`)
eventBus.on('click', clickFn2)
const clickFn3 = res => console.log(`${res} once 事件 3`)
eventBus.once('click', clickFn3)
eventBus.emit('click', '触发了点击')
console.log('############')
//第二种事件
const scrollFn1 = res => console.log(`${res} 1`)
eventBus.on('scroll', scrollFn1)
const scrollFn2 = res => console.log(`${res} once 事件 2`)
eventBus.once('scroll', scrollFn2)
eventBus.emit('scroll', '触发了scroll')
console.log('############')
//删除事件
eventBus.off('click', clickFn2)
eventBus.emit('click', '触发了点击')
console.log('############')
//删除事件
eventBus.off('scroll')
eventBus.emit('scroll', '触发了scroll')
应用场景
- DOM 事件
- 异步回调函数(定时器,promise.then)
- 组件生命周期
- Vue 的响应式
- Vue
$emit
watch