通过例子理解观察者模式和发布订阅模式的区别
观察者模式
Subject 是一个主题,Observer 是一个观察者。观察者可以订阅主题,主题发生变化会通知观察者。这是一个典型的观察者模式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
class Subject { constructor() { // 订阅主题的观察者, subscriber this.subs = [] } subscribe(sub) { // 订阅 this.subs.push(sub) } unsubscribe(sub) { // 取消订阅 const index = this.subs.indexOf(sub) if (index > -1) { this.subs.splice(index, 1) } } fire() { // 主题变化,通知订阅主题的观察者 this.subs.forEach(sub => { sub.notify() }) } } class Observer { constructor(data) { this.data = data } notify() { console.log(this.data) } } let subject = new Subject() let ob1 = new Observer('hello') let ob2 = new Observer('world') subject.subscribe(ob1) subject.subscribe(ob2) subject.fire() |
DOM 事件中的观察者模式,这里回调函数就是一个观察者,订阅了 body 的 click 事件。所以当 body 触发 click 时,会触发回调。
1 2 3 4 5 6 7 |
document.body.addEventListener( 'click', function(e) { console.log('click body') }, false ) |
修改观察者模型,用 Dom 模拟上述例子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
class Dom { constructor() { // 订阅事件的观察者 this.events = {} } /** * 添加事件的观察者 * @param {String} event 订阅的事件 * @param {Function} callback 回调函数(观察者) */ addEventListener(event, callback) { if (!this.events[event]) { this.events[event] = [] } this.events[event].push(callback) } removeEventListener(event, callback) { if (!this.events[event]) { return } const callbackList = this.events[event] const index = callbackList.indexOf(callback) if (index > -1) { callbackList.splice(index, 1) } } /** * 触发事件 * @param {String} event */ fireEvent(event) { if (!this.events[event]) { return } this.events[event].forEach(callback => { callback() }) } } const handler = () => { console.log('fire click') } const dom = new Dom() dom.addEventListener('click', handler) dom.addEventListener('move', function() { console.log('fire click2') }) dom.fireEvent('click') |
发布订阅模式
与观察者模式相比,发布订阅模式中间多了一层处理机制,用于解耦发布者和订阅者之间的关联
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
class EventChannel { constructor() { // 主题 this.subjects = {} } hasSubject(subject) { return this.subjects[subject] ? true : false } /** * 订阅的主题 * @param {String} subject 主题 * @param {Function} callback 订阅者 */ on(subject, callback) { if (!this.hasSubject(subject)) { this.subjects[subject] = [] } this.subjects[subject].push(callback) } /** * 取消订阅 */ off(subject, callback) { if (!this.hasSubject(subject)) { return } const callbackList = this.subjects[subject] const index = callbackList.indexOf(callback) if (index > -1) { callbackList.splice(index, 1) } } /** * 发布主题 * @param {String} subject 主题 * @param {Argument} data 参数 */ emit(subject, ...data) { if (!this.hasSubject(subject)) { return } this.subjects[subject].forEach(callback => { callback(...data) }) } } const channel = new EventChannel() channel.on('update', function(data) { console.log(`update value: ${data}`) }) channel.emit('update', 123) |
这里通过 emit 向主题 update 发布一条消息, 在通过 on 来订阅 update 的主题来接受消息。
参考
- https://molunerfinn.com/observer-vs-pubsub-pattern/#订阅发布模式
- https://juejin.im/post/5a14e9edf265da4312808d86
原文http://cangmean.me/2018/08/31/js-observer/#%E5%8F%91%E5%B8%83%E8%AE%A2%E9%98%85%E6%A8%A1%E5%BC%8F