JavaScript 设计模式之观察者模式

Author Avatar
Klein 8月 28, 2018

介绍

  • 发布 & 订阅
  • 一对多(1对N)

UML

代码演示

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() {
this.state = 0
this.observers = []
}
getState() {
return this.state
}
setState() {
this.state = state
this.notifyAllObservers()
}
notifyAllObservers() {
this.observers.forEach(observer => {
observer.update()
});
}
attach(observer) {
this.observers.push(observer)
}
}

// 观察者
class Observer {
constructor(name, subject) {
this.name = name
this.subject = subject
this.subject.attach(this)
}
update() {
console.log(`${this.name} update, state: ${this.subject.getState()}`)
}
}

// 测试

let s = new Subject()
let o1 = new Observer('o1', s)
let o2 = new Observer('o2', s)
let o3 = new Observer('o3', s)

s.setState(1)

场景

网页事件绑定

1
2
3
4
5
6
7
8
9
10
11
12
<button id="btn1">btn</button>
<script>
$('#btn1').click(function () {
console.log(1);
})
$('#btn1').click(function () {
console.log(2);
})
$('#btn1').click(function () {
console.log(3);
})
</script>

Promise

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
function loadImg(src) {
var promise = new Promise(function(resolve, reject) {
var img = document.createElement('img')
img.onload = function() {
resolve(img)
}
img.onerror = function() {
reject('图片加载失败')
}
img.src = src
})
return promise
}


var src = ''
var result = loadImg(src)
result.then(function(img) {
console.log('width', img.width);
return img
}).then(function(img) {
console.log('height', img.height);
})

jQuery callbacks

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var callbacks = $.Callbacks()
callbacks.add(function(info) {
console.log('fn1', info);
})
callbacks.add(function(info) {
console.log('fn2', info);
})
callbacks.add(function(info) {
console.log('fn3', info);
})

// 测试
callbacks.fire('gogogo')

// fn1 gogogo
// fn2 gogogo
// fn3 gogogo

nodejs 自定义事件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
const EventEmitter = require('events').EventEmitter

const emitter = new EventEmitter()

// 监听 some 事件
emitter.on('some', info => {
console.log('fn1', info);
})
emitter.on('some', info => {
console.log('fn2', info);
})
emitter.on('some', info => {
console.log('fn3', info);
})

// 触发 some 事件
emitter.emit('some', 'xxx')
// fn1 xxx
// fn2 xxx
// fn3 xxx

另外一个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const EventEmitter = require('events').EventEmitter

class Dog extends EventEmitter {
constructor(name) {
super()
this.name = name
}
}

let fenggou = new Dog('fenggou')
fenggou.on('bark', function() {
console.log(this.name, 'barked');
})

setInterval(function() {
fenggou.emit('bark')
})

nodejs 处理 http 请求; 多进程通讯

Vue watch

Vue 和 React 生命周期触发

设计原则验证

  • 主题和观察者分离,不是主动触发而是被动监听,两者解耦
  • 符合开放封闭原则