观察者模式
使用观察者模式的一个常见的场景是: 一个观察对象需要通知一组相互独立的观察者发生变化, 并且这个观察对象和观察者之间是松耦合的关系。观察者模式是由观察者和观察对象组成的, 观察对象维护一组观察者,这组观察者依赖观察对象, 当观察对象的状态发生变化的时候, 会自动通知这组观察者发生动作。一个观察对象由属于自己的一组观察者, 观察对象的作用是注册, 删除观察者, 以及在合适的时机触发观察者。在其他书籍中对于观察者模式的一些定义如下:"One or more observers are interested in the state of a subject and register their interest with the subject by attaching themselves. When something changes in our subject that the observer may be interested in, a notify message is sent which calls the update method in each observer. When the observer is no longer interested in the subject's state, they can simply detach themselves."在观察者模式中, 存在下面四种组成部分:
- 观察对象:维护一组观察者, 可以新增 / 删除观察者, 通知观察者发生变化的逻辑
- 观察者: 提供一个当观察者对象状态发生变化的时候进行状态变化的一个接口
- 观察对象实例: 当相关状态发生变化的时候通知观察者,
- 观察者实例: 具体化一个数据改变的接口, 这个观察者实例是和观察对象相关联的
1 | // 一组观察者, 扩展了一些删除, 添加观察者的一些方法 |
和发布/订阅模式的区别
上面的代码使用发布/订阅模式实现如下:1 | class EventMananger { |
- 对于观察者模式, 其订阅和发布消息是在同一个对象上面进行的(subject), 对于发布订阅模式, 其角色有三种, 订阅者, 发布者 以及 中间存放方法的事件列表。发布者向事件列表中推送消息, 订阅者从事件列表中订阅消息, 并且当发布者推送消息的时候来触发订阅的事件。
- 发布订阅模式发布者和订阅者之间不存在耦合关系, 对于观察者模式, 观察者依赖观察对象, 这两者之间是松耦合关系
- 发布订阅模式应用于跨应用的情况下, 当多个应用之间进行通信的时候, 可以使用这种模式是实现通信, 对于 观察者模式, 主要应用于单个应用的情况。
- 观察对象不知晓观察者的状态, 当某个观察者中包含有一些自增代码的时候, 调用 观察对象的 update 方法,可能会造成观察者状态的重复更新。
两种方式在 vue 中的应用:
在 vue 源码中的$emit
$on
方法中使用了发布/订阅模式:vm._events
作为事件通道, 存放函数列表。$on
订阅者1 | Vue.prototype.$on = function (event, fn) { |
$emit
发布者 1 | Vue.prototype.$emit = function (event) { |
defineReactive
函数, 这个函数是实现当数据更新时相关依赖进行更新的关键, 这个函数里面对于数据的 getter
和 setter
函数内进行了一些处理, 当触发 getter
函数(读取)的时候会进行依赖收集, 当 触发 setter
函数(设置)的时候会通知相关依赖进行更新。具体代码如下:1 | function defineReactive (obj, key, val, customSetter, shallow) { |
dep
是 用来维护者一组的观察者, 通过 dep.depend
方法收集到依赖, 然后通过 dep.notify()
来通知变动。具体到 Dep
的类:1 | export default class Dep { |
depend
方法中的 addDep
方法引用的是 Watcher
实例上面的方法:1 | /** |
Watcher
的实例, Watcher
位于源码中的 Watcher.js
文件中, 一些更新观察者信息的方法作为实例方法。在 Watcher
的类中, 上面定义了update
的方法:1 | update () { |
tips
- 可以在数组中查找到对象, 或者说, js 的数组中存放的是对象的引用:
1
2
3
4let a = {};
let arr = [{}, a];
arr.indexOf(a);
// 1