javascript 观察者模式和发布订阅模式

通过例子理解观察者模式和发布订阅模式的区别

观察者模式

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 的主题来接受消息。

参考

原文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

猜你喜欢

转载自blog.csdn.net/sinat_17775997/article/details/88172103