概念
给对象做代理,对target
的操作进行拦截,比 Object.defineProperty
(或Object.defineProperties
)更更强大。既然Proxy可以对target
的操作进行拦截,那至少会包含取值和赋值操作,下面来看看proxy是如何代理的。
const obj = new Proxy({}, {
get(target, prop, receiver) {
// receiver 指向的是调用这个函数的对象
return 'dva';
},
set(target, prop, value, receiver) {
}
});
obj.a // dva receiver指向obj
obj.b // dva
let child = {};
Object.setPrototypeOf(child, obj);
child.a // dva receiver指向child
上述代码通过new Proxy
得到一个proxy
实例,然后对其如何取值,得到的都是‘dva’
。可以明显看出,这比Object.defineProperty
强大的多,Object.defineProperty
在对target
属性进行拦截时,必须要指定键值来声明,而Proxy
是只要是对其实例进行的操作,都会反映在实例的handler
中。
Proxy构造函数接受两个参数,第一个是target
,表示要被代理的对象。第二个是一个handler
对象,其一些键值是对target
的操作的拦截函数。返回一个Proxy实例。
Proxy可以拦截的操作
get(target, prop, receiver)
拦截对target的取值操作。set(target, prop, value, reciver)
拦截对target的赋值操作。apply(target, obj, args)
拦截 Proxy 实例作为函数调用的操作,即proxy(...args)
,proxy.call(obj, ...args)
,proxy.apply(obj, args)
等。construct(target,args)
拦截 Proxy 实例作为构造函数调用的操作,比如new proxy(...args)
。has(target, prop)
拦截in
操作符,返回一个Boolean值。deleteProperty(target, prop)
拦截 delete 操作符,返回一个Boolean值。ownKeys(target)
拦截Object.getOwnPropertyNames(proxy)
、Object.getOwnPropertySymbols(proxy)
、Object.keys(proxy)
、for...in
循环,返回一个数组。该方法返回目标对象所有自身的属性的属性名,而Object.keys()
的返回结果仅包括目标对象自身的可遍历属性。getOwnPropertyDescriptor(target, prop)
拦截Object.getOwnPropertyDescriptor(proxy, propKey)
,返回属性的描述对象。defineProperty(target, prop, desc)
拦截Object.defineProperty(proxy, propKey, propDesc)
、Object.defineProperties(proxy, propDescs)
,返回一个布尔值。preventExtensions(target)
拦截Object.preventExtensions(proxy)
,返回一个布尔值。getPropertyOf(target)
拦截Object.getPrototypeOf(proxy)
,返回一个对象。setPropertyOf(target)
拦截Object.setPrototypeOf(proxy)
,返回一个布尔值。如果目标对象是函数,那么还有两种额外操作可以拦截。isExtensible(target)
拦截Object.isExtensible(proxy)
,返回一个布尔值。 :::info 在handler中声明拦截函数,然后利用Proxy来实现所能想到的操作 :::Proxy实例方法
Proxy.revocable()
创建一个可以被销毁的Proxy
实例。返回的对象中包含一个proxy
实例和revoke
函数,revoke
函数用来销毁Proxy
实例。 ```javascript const { proxy, revoke} = Proxy.revocable({}, {});
revoke(); // 销毁proxy实例 proxy.a //// TypeError: Revoked
当执行`revoke`函数之后,再访问`Proxy`实例,就会抛出一个错误。<br />**使用场景:**
1. 目标对象不允许直接访问,可以使用Proxy.revocable()做代理,访问结束后,销毁代理。
<a name="CcVoX"></a>
## this问题
proxy对`target`不是透明代理,在不设置任何拦截时,通过proxy调用target中的方法,其中的this会指向proxy实例
```javascript
const target = {
add() {
console.log(this === proxy); // true
}
}
const proxy = new Proxy(target, {});
proxy.add()
如果想要this指向target,可以在对取值进行拦截,绑定this,然后调用。
可能的应用场景
- 在worker线程中,使用proxy异步访问DOM?