学了proxy以后,感觉这是一个很有用的东西。一个最大的功能应该就是能实现对于对象的监听。自己写了一个类,来实现这个功能。下面是代码。


class Watch { // 生成一个watch类 constructor() { this.emit =
dispatchEvent.bind(document); this.on = addEventListener.bind(document);
this.eventUpdated = new Event('updated'); this.eventRead = new Event('read');
this.eventChanged = new Event('changed'); } //对事件的方法进行初始化。 createProxy(obj) {
//用此函数来生成一个proxy对象 let _this = this; return new Proxy(obj, { get(target, prop)
{ _this.emit(_this.eventRead); //在get属性时触发read事件 return target[prop]; },
set(target, prop, val) { let oldVal = target[prop]; target[prop] = val;
if(oldVal != val) _this.emit(_this.eventChanged);
//在set属性时,如果新值和旧值不一样,触发changed事件 _this.emit(_this.eventUpdated);
//在set属性时,触发updated事件 } }); } on(eventStr, callback) { //监听事件并且回调
this.on(eventStr, function () { callback(); }); } }
用法
let obj = { name: 'ss', age: 10, } //声明一个对象 let watchable = new Watch();
//生成一个watch实例 let proxy = watchable.createProxy(obj); //对obj生成一个proxy对象
watchable.on('updated', () => { console.log('updated') console.log(obj) });
//当proxy的属性被读取的时候触发回调 watchable.on('read', () => { console.log('read');
console.log(obj) })//当proxy属性被设置的时候触发回调 watchable.on('changed', () => {
console.log('changed') console.log(obj) })//当proxy属性被重新设置并且值不一样的时候触发回调
proxy.name = 'sss' //changed {name: "sss", age: 10} updated {name: "sss", age:
10}setTimeout(() => { proxy.age;}, 2000); // read {name: "sss", age: 10}
顺便提一下,这个类有一个bug, 如果你是对象的嵌套,可能就没有用了。比如
let obj = { name:{ first:'ss', last:'ee' } } let watcher = new Watch(); let
proxy = watcher.createProxy(obj); watcher.on('updated', function(){
console.log(obj); }); watcher.on('read', function(){ console.log(obj); });
proxy.name.first = 'xxx'

但是有个折中的办法,就是你可以通过read事件来判断嵌套对象的属性。因为你要set嵌套对象的属性,必须先要读取这个嵌套的对象,就可以导致触发read事件。当然,这会导致一些不必要的事件触发,需要一些条件判断。




~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

嵌套对象的监听问题还是一直困扰了我,这种折中的方法并不好。
所以今天又花了一天的时间来研究如何对对象进行深度代理。结果想到了用递归的方法,解决了此问题。下面是修改过的类:
class Watch { constructor() { this.emit = dispatchEvent.bind(document);
this.listen = addEventListener.bind(document); this.eventUpdated = new
Event('updated'); this.eventRead = new Event('read'); this.eventChanged = new
Event('changed'); } createProxy(obj) { let _this = this; return addProxy(obj);
//这里的addProxy 函数是一个递归函数。 function addProxy(...args) { let handler = { get(t, p)
{ _this.emit(_this.eventRead); addProxy(t, p); //对嵌套对象进行代理 return t[p] },
set(t, p, v) { let oldValue = t[p]; t[p] = v; if (t[p] != oldValue)
_this.emit(_this.eventChanged); _this.emit(_this.eventUpdated); } } if
(args.length == 2 && typeof args[0][args[1]] == 'object') { args[0][args[1]] =
new Proxy(args[0][args[1]], handler);//判断对象的属性是不是一个嵌套的对象,如果是则代理 } else { let
proxy = new Proxy(args[0], handler);//这是对最外围的对象进行代理 return proxy; } } }
on(eventStr, callback) { if (!/,+/.test(eventStr)) { this.listen(eventStr, ()
=> callback()); } else { let eventStrArr = eventStr.split(','); for (let i = 0,
len = eventStrArr.length; i < len; i++) { this.listen(eventStrArr[i].trim(), ()
=> callback());//这里只是让你可以写wather.on('updated, read', callback) } } } }
这里还是有个问题,这个代理会一定程度上污染对象内的属性。不过如果你直接把对象赋值成代理对象来用的话就不会纠结于此。下面是用法:
let obj = { name: { first: { second: { third: 'ss' } } }, age: { age1:{ age2:
{ age3: 10 } } } } let watcher = new Watch(); obj = watcher.createProxy(obj);
watcher.on('read, updated', () => { console.log(obj) });
obj.name.first.second.third = 'ssss'; //一连串的触发监听 obj.age.age1.age2.age3 = 20;
//一连串的触发监听,前面都是read, 最后一个是updated



这是监听触发的结果,可以看到每次get属性,以及set属性都有被监听到。




顺带推荐一下,这篇文章写得很好,用于加深理解

点击打开链接
<http://pinggod.com/2016/%E5%AE%9E%E4%BE%8B%E8%A7%A3%E6%9E%90-ES6-Proxy-%E4%BD%BF%E7%94%A8%E5%9C%BA%E6%99%AF/>