上周我们探讨了相关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 = '李四'; 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 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 ), fns = this.clientList[ key ]; if ( !fns || fns.length === 0 ){ return false; } for( var i = 0, fn; fn = fns[ i++ ]; ){ fn.apply( this, arguments ); } } };
|
综述:
发布—订阅模式的优点非常明显,一为时间上的解耦,二为对象之间的解耦。它的应用非常广泛,既可以用在异步编程中,也可以帮助我们完成更松耦合的代码编写。发布—订阅模式还可以用来帮助实现一些别的设计模式,比如中介者模式。 从架构上来看,无论是MVC还是MVVM,都少不了发布—订阅模式的参与,而且JavaScript本身也是一门基于事件驱动的语言。
当然,发布—订阅模式也不是完全没有缺点。创建订阅者本身要消耗一定的时间和内存,而且当你订阅一个消息后,也许此消息最后都未发生,但这个订阅者会始终存在于内存中。另外,发布—订阅模式虽然可以弱化对象之间的联系,但如果过度使用的话,对象和对象之间的必要联系也将被深埋在背后,会导致程序难以跟踪维护和理解。特别是有多个发布者和订阅者嵌套到一起的时候,要跟踪一个bug不是件轻松的事情。