[译]ES6中的代理对象
原文:http://ariya.ofilabs.com/2013/07/es6-and-proxy.html
能够拦截在一个对象上的指定操作的能力是非常有用的,尤其是在故障调试的时候.通过ECMAScript 6中的新特性——代理(proxy),这种能力才最终得以实现.
在目前最新的ES6规范草案中(2013年5月14日发布,第15次修订版),第15.18小节——代理对象(Proxy Objects)这部分的文档仍然是空的.不过随着规范的日趋稳定,这里会补充上完整的参考文档的.目前,可以在ES wiki上的直接代理(Direct Proxies)页面内找到最详细的资料.在我写这篇文章的时候,只有Firefox 18+实现了最新的代理规范(Chrome实现了一个旧版的已经被废弃的代理规范——Proxy.create).
演示代理特性最好的方式就是通过下面这个简单的例子:
var engineer = { name: 'Joe Sixpack', salary: 50 }; var interceptor = { set: function (receiver, property, value) { console.log(property, 'is changed to', value); receiver[property] = value; } }; engineer = Proxy(engineer, interceptor);
在上面的代码中,我们首先创建了一个简单的对象engineer和另外一个对象interceptor.然后,
engineer对象被另外一个由Proxy()函数构建的代理对象所代替
.传入Proxy()的第二个参数interceptor是一个处理器(handler)对象.一个处理器对象可以包含有多种处理方法,在这个例子中只有一种处理方法,就是set.set处理方法能够拦截到那些在代理对象身上进行的所有的属性赋值操作
.
让我们看看执行下面的这句赋值语句时会发生什么:
engineer.salary = 60;
这时,先前设置好set处理方法将会被调用
.因此,会输出这样一条信息:
salary is changed to 60
每当代理对象engineer上的一个属性被赋值时,处理器对象
interceptor都会得到通知.只要提前设置好对应的处理方法(set),我们就能够拦截到这一操作,实现自己想要的功能.当然,不只是属性的赋值操作,代理对象身上的其他操作,比如属性的读取,属性的删除,属性的遍历等操作也都可以使用set之外的其他处理方法来进行拦截.除了调试用途,代理对象还可以用到那些需要数据绑定(data binding)功能的框架中.我们可以把数据模型(data model)设置成为一个代理对象,监控它的属性的值的变化.也就不需要再使用其他的语法(比如必须使用显示的set方法来更改模型的值
)或者持续追踪模型的值的变化(比如脏状态检查(dirty state checking))了.
你觉的代理还有什么用呢?
译者注:关于调试用途,我讲一个我自己的真实案例,情节稍有简化.就在前两天,公司的项目中出现一个bug,需要我来解决.我分析出是一个对象的某个属性在运行途中被误删除导致,由于用的是模块化开发,这个对象又被多个模块中的代码处理过,我很难找到这个delete语句到底在哪个文件中.虽然可以在本页面内加载的十几个js文件中搜索delete然后一一排除,但我决定试一下更高端的调试方法.
我在这个对象刚刚生成的代码位置处的下一行写下一句:
obj.watch("prop", function(){console.log(arguments.callee.caller)})
我使用了Firefox特有的watch方法来找出到底哪个函数删掉了obj对象的prop属性,但情况出乎我意料,刷新页面后没有任何东西输出.于是我查一查MDN,原来watch只能监测到属性的变更,而不会监测到属性的删除.关键是这个MDN页面我去年翻译过啊,但我当时没注意到这个细节.
于是我又使出Firefox特有的第二个特性,Proxy.啪啪啪打出这样一句:
obj = Proxy(obj, {deleteProperty : function(){console.log(arguments.callee.caller)}})
就这样成功找到了那个想要找的函数,这应该就是作者一开始所说的故障调试的用途了.