首先看一个例子:
var outerT=function(){return 'I am qinliang'}
var handler={
apply:function(target,ctx,args){
//第一个参数为目标对象
return 'ctx='+ctx.name+",args="+args+",target="+(target===outerT);
}
}
var p=new Proxy(outerT,handler);
console.log(p.apply({name:'qinliang'},[1,2,3]));
打印结果为ctx=qinliang,args=1,2,3,target=true,因此target就是目标对象,ctx就是为目标对象传递的上下文,args就是为目标对象传递的额外参数。这个例子所有的拦截都在代理中被处理了,如何把代理中的处理结果传入到最终的对象上继续调用,见下面的例子:
var outerT=function(left,right){
console.log(this);
return left+right;
}
var handler={
apply:function(target,ctx,args){
//第一个参数为目标对象,这里通过Reflect.apply把上次处理的结果传递给被代理执行
return Reflect.apply(...arguments)*2;
}
}
var p=new Proxy(outerT,handler);
console.log(p.apply({name:'qinliang'},[2,3]));
//通过apply传入了上下文
console.log(p(2,3));
//直接调用也会被拦截,上下文为window
console.log(Reflect.apply(p,null,[2,3]));
//通过Reflect.apply也会被调用,上下文为window
注意:Reflect.apply(fn, obj, args)相当于Function.prototype.apply.call(fn, obj, args),其中obj就是上下文的,而args就是传入的参数!
has函数用于隐藏某些属性从而不被in操作符发现,而enumerate用于拦截for..in操作符:
var obj={a:10};
Object.preventExtensions(obj);
var p=new Proxy(obj,{
has:function(target,prop){
return false;
}
})
console.log(a in p);
//如果原对象不可配置或者禁止扩展,这时候has会报错
constructor用于拦截new操作符,如果返回值不是对象会抛出错误
var p=new Proxy(function(){},{
construct:function(target,argumentsList){
//这里不是数组,即使你new p(1,2);时候传入了多个对象
console.log(argumentsList.join(','));
return {value:argumentsList[0]*10}
}
})
new p(1,2);
isExtensible方法拦截Object.isExtensible操作
var p=new Proxy({},{
isExtensible:function(target){
//拦截Object.isExtensible方法
return false;
}
})
Object.isExtensible(p);
注意:这个方法有一个限制,Object.isExtensible(proxy)===Object.isExtensible(target),如果不满足这个条件就会抛出错误!这里就会报错
preventExtensions用于拦截Object.preventExtensions方法:
var p=new Proxy({},{
preventExtensions:function(target){
console.log('called');
Object.preventExtensions(target);
//这里必须手动调用让target无法扩展,后面的 Object.preventExtensions(p);才能调用
return true;
}
})
Object.preventExtensions(p);
Proxy.revocable方法
let target={};
let handler={};
let {proxy,revoke}=Proxy.revocable(target,handler);
proxy.foo=123;
console.log(proxy.foo);
//打印123
revoke();
console.log(proxy.foo);
//报错
这个方法返回一个对象,其中proxy就是产生的Proxy实例,而revoke是一个函数,可以取消Proxy实例
Reflect和Proxy对象一样,也是ES6为了操作对象而提供的新的API:
(1)将Object对象一些明显属于语言层面的方法放在Reflect对象上,现阶段某些方法同时存在于Object和Reflect对象上,未来只会部署在Reflect对象上
(2)修改了某些Object方法返回值让他变得更加合理。如Object.defineProperty在无法定义属性时候抛出错误,但是Reflect.defineProperty返回false
(3)Object操作都变成函数行为。某些Object操作是命令式的,如name in obj和delete obj[name],而Reflect.has,Reflect.deletePropery变成函数行为
(4)Reflect方法和Proxy方法一一对应,只要是Proxy对象上存在那么就能在Reflect中找到,于是Proxy对象可以方便的调用对应的Reflect方法完成默认行为,作为修改行为的基础。也就是说,不管Proxy如何修改默认行为,你总是可以在Reflect上获取默认行为
我们看看Proxy和Reflect中最重要的一个参数receiver的用法:
var obj={
get foo(){return this.bar();},
bar:function(){
console.log('obj bar');
}
}
var wrapper={
bar:function(){
console.log('wrapper bar');
}
}
Reflect.get(obj,'foo',wrapper);
//这时候把this.bar修改为wrapper.bar,这时候打印wrapper bar