小知识,大挑战!本文正在参与“程序员必备小知识”创作活动
开发者常常将订阅发布模式和观察者模式混淆,其实这两者存在一些实现上的差异,常常混淆的原因是因为其概念的通用
订阅发布是⼀种消息范式,消息的发送者(称为发布者)不会将消息直接发送给特定的接收者(称为订阅者)。⽽是将发布的消息分为不同的类别,然后分别发送给不同的订阅者。在该模式中会存在三个角色:发布者、订阅者和通道(用于控制消息的订阅和发布)
观察者模式是⼀种⼀对多的关系,让多个观察者对象同时监听某⼀个主题对象,这个主题对象的状态发⽣变化时就会通知所有的观察者对象,使得它们能够⾃动更新⾃⼰。在该模式中会存在两个角色:观察者和被观察者
订阅发布
首先看一下常规的订阅发布实现
function EventEmitter() {
this._events = Object.create(null)
}
// 订阅
EventEmitter.prototype.on = function (eventName, fn) {
// 若不是直接 new EventEmitter,而是通过继承,可能导致没有 _events,需要处理一下
if(!this._events) {
this._events = Object.create(null)
}
// 处理 newListener
if(eventName !== 'newListener'){
this.emit('newListener', eventName)
}
if(this._events[eventName]){
this._events[eventName].push(fn)
}else {
this._events[eventName] = [fn]
}
}
// 发布
EventEmitter.prototype.emit = function (eventName, ...args) {
if(this._events[eventName]){
this._events[eventName].forEach(fn => fn(...args))
}
}
// 订阅 - 仅触发一次
EventEmitter.prototype.once = function (eventName, fn) {
const one = (...args) => {
fn(...args)
this.remove(eventName, one)
}
one.fn = fn
this.on(eventName, one)
}
// 移除订阅
EventEmitter.prototype.remove = function (eventName, fn) {
if(this._events[eventName]){
this._events[eventName] = this._events[eventName].filter(cb => cb !== fn && cb.fn !== fn)
}
}
验证一下效果
const em = new EventEmitter();
em.on("one", () => {
console.log("触发one事件");
});
em.on("one", () => {
console.log("触发one事件");
});
em.emit("one");
控制台输出两次触发one事件
观察者
接下来实现观察者模式
/**
* 被观察者
*/
class Subject {
constructor(name) {
this.name = name
this.arr = []
this.state = '正常'
}
attach(o) {
this.arr.push(o)
}
setState (newState){
this.state = newState;
this.arr.forEach(o => {
o.update(newState);
});
}
}
/**
* 观察者
*/
class Observer {
constructor(name) {
this.name = name;
}
update(newState) {
console.log(`观察者${this.name}接收到了被观察者的信息${newState}`);
}
}
验证一下效果
const s = new Subject()
const o1 = new Observer('nordon')
const o2 = new Observer('wy')
s.attach(o1)
s.attach(o2)
s.setState('新的状态')
控制台会输出:
观察者nordon接收到了被观察者的信息新的状态
观察者wy接收到了被观察者的信息新的状态
通过attach
添加的被观察者自动触发
综上可以看出两者存在的区别还是较为显著的,借用一句话概括两者的区别:有没有中间商赚差价