观察者模式
实现观察者模式,理解发布-订阅机制
问题
实现观察者模式,让多个观察者监听一个主题对象,当主题状态变化时自动通知所有观察者。
解答
基础实现
// 主题(被观察者)
class Subject {
constructor() {
this.observers = []
}
// 添加观察者
addObserver(observer) {
this.observers.push(observer)
}
// 移除观察者
removeObserver(observer) {
this.observers = this.observers.filter(obs => obs !== observer)
}
// 通知所有观察者
notify(data) {
this.observers.forEach(observer => observer.update(data))
}
}
// 观察者
class Observer {
constructor(name) {
this.name = name
}
update(data) {
console.log(`${this.name} 收到通知:`, data)
}
}
// 使用
const subject = new Subject()
const observer1 = new Observer('观察者1')
const observer2 = new Observer('观察者2')
subject.addObserver(observer1)
subject.addObserver(observer2)
subject.notify({ message: '状态更新了' })
// 观察者1 收到通知: { message: '状态更新了' }
// 观察者2 收到通知: { message: '状态更新了' }
subject.removeObserver(observer1)
subject.notify({ message: '再次更新' })
// 观察者2 收到通知: { message: '再次更新' }
EventEmitter 实现
class EventEmitter {
constructor() {
this.events = {}
}
// 订阅事件
on(event, callback) {
if (!this.events[event]) {
this.events[event] = []
}
this.events[event].push(callback)
return this
}
// 取消订阅
off(event, callback) {
if (!this.events[event]) return this
this.events[event] = this.events[event].filter(cb => cb !== callback)
return this
}
// 触发事件
emit(event, ...args) {
if (!this.events[event]) return this
this.events[event].forEach(callback => callback(...args))
return this
}
// 只订阅一次
once(event, callback) {
const wrapper = (...args) => {
callback(...args)
this.off(event, wrapper)
}
this.on(event, wrapper)
return this
}
}
// 使用
const emitter = new EventEmitter()
emitter.on('login', user => {
console.log(`${user} 登录了`)
})
emitter.once('firstVisit', () => {
console.log('首次访问,显示引导')
})
emitter.emit('login', '张三') // 张三 登录了
emitter.emit('firstVisit') // 首次访问,显示引导
emitter.emit('firstVisit') // 无输出,因为用的 once
实际应用:数据绑定
// 简单的响应式数据
function reactive(obj, callback) {
return new Proxy(obj, {
set(target, key, value) {
const oldValue = target[key]
target[key] = value
// 值变化时通知
if (oldValue !== value) {
callback(key, value, oldValue)
}
return true
}
})
}
// 使用
const state = reactive({ count: 0 }, (key, newVal, oldVal) => {
console.log(`${key}: ${oldVal} -> ${newVal}`)
})
state.count = 1 // count: 0 -> 1
state.count = 2 // count: 1 -> 2
关键点
- 一对多关系:一个主题对应多个观察者,解耦发布者和订阅者
- 主动推送:状态变化时主动通知,观察者无需轮询
- EventEmitter:Node.js 和浏览器中广泛使用的事件机制
- 应用场景:DOM 事件、Vue 响应式、Redux 状态管理、WebSocket 消息处理
- 与发布订阅区别:观察者模式是直接通知,发布订阅有中间调度中心
目录