使用Proxy,Reflect实现观察者模式

作者 johansen 日期 2017-03-23
使用Proxy,Reflect实现观察者模式

上周我们探讨了相关ES6 的 Proxy ,并由此引入相关代理模式的思考,今天我们继续上周ES6的学习,去思考由此引发的JS设计方面的考量。

利用ES6 Proxy 实现观察者模式

观察者模式:
它定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知。

举个例子 :

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
const person = observable({
name: '张三',
age: 20
});
function print() {
console.log(`${person.name}, ${person.age}`)
}
observe(print);
person.name = '李四';
// 输出
// 李四, 20
var queuedObservers = new Set();
var observe = function observe(fn) {
return queuedObservers.add(fn);
};
var observable = function observable(obj) {
return new Proxy(obj, { set: set });
};
function set(target, key, value, receiver) {
var result = Reflect.set(target, key, value, receiver);
queuedObservers.forEach(function (observer) {
return observer();
});
return result;
}

上面代码中,数据对象person是观察目标,函数print是观察者。一旦数据对象发生变化,print就会自动执行。利用proxy的set方法捕获相关对象,然后利用Reflect实现最初的方法。proxy的Set中实现的是依赖的对象的设置。

发布订阅模式的作用

  1. 说明发布—订阅模式可以广泛应用于异步编程中。
  2. 发布—订阅模式可以取代对象之间硬编码的通知机制,一个对象不用再显式地调用另外一个对象的某个接口。

发布订阅模式通用实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
var event = {
clientList: [],//存放订阅事件
listen: function( key, fn ){
if ( !this.clientList[ key ] ){
this.clientList[ key ] = [];
}
this.clientList[ key ].push( fn ); // 订阅的消息添加进缓存列表,注册
},
trigger: function(){ //发布,触发
var key = Array.prototype.shift.call( arguments ), // (1);
fns = this.clientList[ key ];
if ( !fns || fns.length === 0 ){ // 如果没有绑定对应的消息
return false;
}
for( var i = 0, fn; fn = fns[ i++ ]; ){
fn.apply( this, arguments ); // (2) // arguments 是trigger时带上的参数
}
}
};

综述:

发布—订阅模式的优点非常明显,一为时间上的解耦,二为对象之间的解耦。它的应用非常广泛,既可以用在异步编程中,也可以帮助我们完成更松耦合的代码编写。发布—订阅模式还可以用来帮助实现一些别的设计模式,比如中介者模式。 从架构上来看,无论是MVC还是MVVM,都少不了发布—订阅模式的参与,而且JavaScript本身也是一门基于事件驱动的语言。

当然,发布—订阅模式也不是完全没有缺点。创建订阅者本身要消耗一定的时间和内存,而且当你订阅一个消息后,也许此消息最后都未发生,但这个订阅者会始终存在于内存中。另外,发布—订阅模式虽然可以弱化对象之间的联系,但如果过度使用的话,对象和对象之间的必要联系也将被深埋在背后,会导致程序难以跟踪维护和理解。特别是有多个发布者和订阅者嵌套到一起的时候,要跟踪一个bug不是件轻松的事情。