Proxy
目录1. Proxy简介
-
Proxy可以理解为在目标对象前架设一个"拦截层",外界对该对象的访问都必须先通过这层拦截,因此提供了一种机制可以对外界的访问进行过滤和改写
-
语法:
var proxy = new Proxy(target, handler)
- target参数表示所要拦截的目标对象,即如果没有Proxy介入,操作原来要访问的就是这个对象
- handler参数是一个对象,用来定义拦截行为。Proxy会根据拦截行为重载方法,即用自己的定义覆盖语言的原始定义
//此处拦截目标对象是一个空对象 var obj = new Proxy({}, { //拦截并重载读取操作 get:function(target, key, receiver){ console.log('getted!') //拦截并处理完成,使用Reflect中的原生get方法 return Reflect.get(target, key, receiver); }, //拦截并重载写入操作 set:function(target, key, value, receiver){ console.log('setted!') //拦截并处理完成,使用Reflect中的原生set方法 return Reflect.set(target, key, value, receiver); } }); obj.count = 1; //setted! ++obj.count; //getted! setted!
-
如果要使拦截生效,必须对Proxy实例对象操作,而不是对目标对象操作。对Proxy对象的操作会影响目标对象,对目标对象的操作却不会触发Proxy的拦截
2. 注意事项
-
Proxy不一定到代理某一个目标对象,它本身也可以作为一个具有拦截操作的对象。如果不想代理某个对象,只要把target设置为空对象
{}
就可以了 -
如果一个属性不可配置(configurable)或不可写(writable),那么该属性不能被代理,通过Proxy对象访问或修改该属性均会报错
-
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
甚至有些原生对象的内部属性只有通过争取的this才能获取,此时使用proxy也无法代理这些原生对象的属性
const target = new Date(); const handler = {}; const proxy = new Proxy(target, handler); proxy.getDate();
3. Proxy支持的所有拦截操作
出于减少记忆成本的目的,除特殊几个拦截操作,其余拦截操作不一一列出,只要记住规律即可:
所拦截的操作大都是Object对象中的同名操作,如getOwnPropertyDescriptor(target, propKey)
所拦截的就是Object.getOwnPropertyDescriptor(proxy,propKey)
。所以只要看到拦截操作名称就能大致知道所拦截的是什么操作
所有拦截操作
-
get(target, propKey, receiver)
- 拦截对象属性的读取,propKey表示被读取的属性,receiver是一个可选对象
-
set(target, propKey, receiver)
- 拦截对象属性的设置,返回一个布尔值
-
has(target, propKey)
- 拦截
propKey in proxy
的操作,返回一个布尔值
- 拦截
-
deleteProperty(target, propKey)
- 拦截
delete proxy[propKey]
- 拦截
-
getOwnPropertyDescriptor(target, propKey)
:返回属性的描述对象 -
preventExtensions(target)
:返回布尔值 -
getPrototypeOf(target)
:返回对象 -
isExtensible(target)
:返回布尔值 -
setPrototypeOf(target, proto)
:返回布尔值 -
defineProperty(target, propKey, propDesc)
- 拦截
Object.defineProperty(proxy, propKey, propDesc)
- 拦截
Object.defineProperties(proxy,propDescs)
- 返回布尔值
- 拦截
-
ownKeys(target)
- 拦截
Object.getOwnPropertyNames(proxy)
- 拦截
Object.getOwnPropertySymbols(proxy)
、 - 拦截
Object.keys(proxy)
- 返回一个数组,包含目标对象所有自身属性的属性名
- 拦截
如果目标对象是函数,那么还有两种额外操作可以拦截
apply(target, object, args)
- 拦截proxy实例,并将其作为构造函数调用的操作,比如
proxy(...args)
、proxy.call(object, ...args)
- 拦截proxy实例,并将其作为构造函数调用的操作,比如
construct(target, args)
- 拦截Proxy实例作为构造函数调用的操作,比如
new proxy(...args)
- 拦截Proxy实例作为构造函数调用的操作,比如
4. 使用示例
-
如果访问目标对象不存在的属性,抛出一个错误而不是单纯的返回undefined
var person = { name: "张三" }; var proxy = new Proxy(person,{ get:function(target,property){ //如果存在,就原样返回值 if(property in target){ return target[property] } else{ throw new ReferenceError("Property \"" + property + "\" dose not exist"); } } }) proxy.name; //"张三" proxy.age; //抛出错误
-
保证Person对象中的age属性是一个不大于200的整数
let handler = { set: function(obj, prop, value){ if(prop === 'age'){ if(!Number.isInteger(value)){ throw new TypeError('The age is not an integer'); } if(value > 200){ throw new RangeError('The age is invaild'); } } obj[prop] = value; } }; let person = new Proxy({},handler); person.age = 100; person.age = 'hi'; //报错 person.age = 200; //报错