行为模式 - 观察者

# 观察者

允许你制定一种订阅机制, 可在对象事件发生时通知多个 "观察" 该对象的其他对象

# 优点

  • 开闭原则 无需修改发布者代码就能引入新的订阅者类

  • 可在运行时建立对象之间的联系

# 缺点

  • 订阅者的通知顺序是随机的

# 适用场景

  • 当一个对象状态的改变需要改变其他对象, 或实际对象是事先未知的或动态变化的时

    例如你创建了自定义按钮类并允许客户端在按钮中注入自定义代码, 这样当用户按下按钮时就会触发这些代码

  • 当应用中的一些对象必须观察其他对象时, 可使用该模式, 但仅能在有限时间内或特定的情况下使用

# 发布订阅实现

点击查看代码
/**
 * 发布订阅
 */
class Emitter {
  constructor() {
    this.emits = {}
  }

  on(emitName, callback) {
    if (!this.emits[emitName]) {
      this.emits[emitName] = []
    }

    if (this.emits[emitName].includes(callback)) {
      // 事件重复注册
      console.log(`${emitName} 事件重复注册!`)
    } else {
      this.emits[emitName].push({
        handler: callback
      })
    }
  }

  once(emitName, callback) {
    if (!this.emits[emitName]) {
      this.emits[emitName] = []
    }

    this.emits[emitName].push({
      handler: callback,
      once: true
    })
  }

  emit(emitName, ...args) {
    if (!this.emits[emitName]) return

    this.emits[emitName].forEach((item, index) => {
      item.handler(...args)

      if (item.once) {
        this.emits[emitName].splice(index, 1)
      }
    })
  }

  off(emitName, callback) {
    if (this.emits[emitName]) {
      if (
        callback &&
        callback instanceof Function &&
        this.emits[emitName].includes(callback)
      ) {
        this.emits[emitName] = this.emits[emitName].filter(v => v.handler !== callback)
      } else {
        delete this.emits[emitName]
      }
    }
  }
}

// ---------- Test ---------
const emitter = new Emitter()

function user1(content) {
  console.log('user1 on', content)
}

function user2(content) {
  console.log('user2 on', content)
}
emitter.on('test', testHandle)
emitter.on('test2', test2Handle)

emitter.emit('test', 'emit...') // user1 on emit...
emitter.emit('test2', 'emit2...') // user2 on emit2...

emitter.off('test', testHandle)
emitter.emit('test', testHandle)  // no output
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
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79

# 常见问题

观察者 VS 发布订阅 (opens new window)