Proxy的相关概念
Proxy用于修改某些操作的默认行为(即对编程语言进行编程,也可称为元编程)
换个角度理解:
把你创建出来的对象比作是大明星,那么Proxy就像是这个大明星的经纪人,
现在有广告商想找这个大明星,那么广告商就得先找经纪人,然后,经纪人觉得,这个广告挺好,
可以接,再去找这个大明星,如果经纪人觉得这个广告商没啥诚意,就可以直接拒绝这个广告商。
proxy充当着这个经纪人的角色,外界对这个对象的访问,都必须先通过这个拦截,因此,也可以对外界的访问进行过滤和改写。
当然,你也会说,广告商会不会不通过经纪人,直接找到这个大明星,这种情况也是存在的。
那么,这种情况下,就没经纪人什么事了,也没有拦截,过滤作用了
即:要使proxy起作用,必须针对proxy实例进行操作,而不是针对目标对象进行操作。(即应该跟经纪人谈,而不是跟大明星谈)
好了,我们现在对proxy有点了解之后,再来看看,怎么使用proxy吧。
在ES6中提供了Proxy构造函数,用来生成Proxy实例。
const proxy = new Proxy(target,handler)
其中:
target就是我们的目标对象(就是上面的例子中的大明星),
handler用来定制拦截行为(也可以理解为给经纪人的工作内容,即让经济人干嘛,做什拦截过滤)。
几个小知识点
tips:
1:如果handler中没有设置拦截操作,则会直接落在目标对象上,按照原先的方式产生结果
(就比如,经纪人光拿工资,不干活,把所有事情扔给大明星一样)
2:当然,指定什么事情,经纪人就做什么,不多也不少(毕竟,经纪人也想下班)
3:proxy实例也可以做为其他对象的原型对象
const proxy = new Proxy({},{
get:function(target,propKey){
return 35;
}
})
let obj = Object.create(proxy);
obj.time //35
这里我们创建一个以proxy为原型的对象,在获取time的时候,obj现在没有这个属性,
就会在原型链上找,而他的原型又是proxy,在proxy读取这个属性的时候,被拦截了,返回35
13种操作
当然经纪人也不是全能的,不一定会所有的事情,目前,经纪人会做的有13种
1:get(target, propKey, receiver):
拦截对象属性的读取,比如proxy.foo和proxy['foo']。
2:set(target, propKey, value, receiver):
拦截对象属性的设置,比如proxy.foo = v或proxy['foo'] = v,返回一个布尔值。
3:has(target, propKey):
拦截propKey in proxy的操作,返回一个布尔值。
4:deleteProperty(target, propKey):
拦截delete proxy[propKey]的操作,返回一个布尔值。
5:ownKeys(target):
拦截Object.getOwnPropertyNames(proxy)、Object.getOwnPropertySymbols(proxy)、
Object.keys(proxy)、for...in循环,返回一个数组。该方法返回目标对象所有自身的属性的属性名,
而Object.keys()的返回结果仅包括目标对象自身的可遍历属性。
6:getOwnPropertyDescriptor(target, propKey):
拦截Object.getOwnPropertyDescriptor(proxy, propKey),返回属性的描述对象。
7:defineProperty(target, propKey, propDesc):
拦截Object.defineProperty(proxy, propKey, propDesc)、
Object.defineProperties(proxy, propDescs),返回一个布尔值。
8:preventExtensions(target):
拦截Object.preventExtensions(proxy),返回一个布尔值。
9:getPrototypeOf(target):
拦截Object.getPrototypeOf(proxy),返回一个对象。
10:isExtensible(target):
拦截Object.isExtensible(proxy),返回一个布尔值。
11:setPrototypeOf(target, proto):
拦截Object.setPrototypeOf(proxy, proto),返回一个布尔值。如果目标对象是函数,那么还有两种额外操作可以拦截。
12:apply(target, object, args):
拦截 Proxy 实例作为函数调用的操作,比如proxy(...args)、proxy.call(object, ...args)、proxy.apply(...)。
13:construct(target, args):
拦截 Proxy 实例作为构造函数调用的操作,比如new proxy(...args)。
更多详情,可以查看:es6.ruanyifeng.com/#docs/proxy
好了,当大明星想炒了这个经纪人了,要怎么做?
Proxy.revocable() ---取消代理
Proxy.revocable()方法返回一个可以取消的Proxy实例
举个例子:
const target = {};
const handler = {};
const {proxy,revoke} = Proxy.revocable(target,handler);
proxy.foo = 123;
proxy.foo //123
revoke();
proxy.foo // TypeError:Revoke
这里:
Proxy.revocable方法返回一个对象,该对象的proxy属性是Proxy实例,revoke()属性是一个函数
可以取消Proxy实例。
所以在执行revoke函数之后,再访问Proxy实例,就会报错。
This指向
虽然Proxy可以代理针对目标对象的访问,但它不是目标对象的透明代理(即不做任何拦截的情况下,也无法保证与目标对象的行为一致)。
主要原因是:目标对象内部的this关键字会指向proxy代理
举个例子:
const target = {
m:function (){
console.log(this === proxy)
}
}
const handler = {};
const proxy = new Proxy(target,handler)
target.m() //false
proxy.m() // true
这里proxy代理target.m,后者内部的this就是指向proxy,而不是target。
还有一些原生对象的内部属性,只有通过正确的this才能拿到,
所以proxy也无法代理这些原生对象属性,不过可以通过绑定原始对象,解决这个问题。