proxy(代理)亦可理解为发言代表
syntax: const proxy = new Proxy(target, handle)
释义:proxy
是生成的发言代表,target
是需要被代表发言的对象,handle
是发言代表需要遵守的规则,也是一个对象。
先抛一个小例子,热热身:
const obj = {a:1}
const handle = {
get: function(target, propKey){
return '我就不告诉你!'
}
}
const proxy = new Proxy(obj, handle)
proxy.a // "我就不告诉你!"
obj.a // 1
看,proxy(代理)可坏了,它得知并拦截了你访问obj的操作(get),还不好好回答你问题!当你见不到obj本人时,你从proxy那获取到的obj信息,其实是proxy的handle胡诌的答案,并不一定真实、有效!
参考阮一峰的《ES6入门》,代理能够拦截的操作有以下13种:
拦截的操作:对象属性的读取,比如proxy.foo和proxy[‘foo’];
参数释义:
target
——目标对象
propKey
——属性名
receiver
——【可选】操作行为所针对的对象。①如果是你通过代理访问target的属性,那么receiver就指向代理;②如果你是以代理为原型新生成了一个对象,则receiver指向该对象。
返回值:想return啥return啥~
示例,这里阮一峰举了三个很好的例子(其实例子有好几个,但是复杂点的就3个),我就不献丑了~
注*: 如果一个属性不可配置(configurable)且不可写(writable),则 Proxy 不能修改该属性的get操作,否则通过 Proxy 对象访问该属性会报错。
拦截的操作:对象属性的设置,比如proxy.foo = v或proxy[‘foo’] = v;
参数释义:
target
——目标对象
propKey
——属性名
value
——属性值
receiver
——【可选】操作行为所针对的对象。
返回值:布尔值。
注*:①如果目标对象自身的某个属性,不可写且不可配置,那么set方法将不起作用;②严格模式下,set代理如果没有返回true,就会报错
拦截的操作:propKey in proxy的操作;
参数释义:
target
——目标对象
propKey
——需查询的属性名
返回值:布尔值。
注*:
①如果原对象不可配置或者禁止扩展,这时has拦截会报错;
②has方法拦截的是HasProperty操作,而不是HasOwnProperty操作,即has方法不判断一个属性是对象自身的属性,还是继承的属性;
③has拦截对for…in循环不生效;
拦截的操作:delete proxy[propKey]
的操作;
参数释义:
target
——目标对象
propKey
——目标对象的属性名
返回值:布尔值。
注*:
①如果这个方法抛出错误或者返回false,当前属性就无法被delete命令删除;
②目标对象自身的不可配置(configurable)的属性,不能被deleteProperty方法删除,否则报错。
拦截的操作:对象自身属性的读取操作。如:
①Object.getOwnPropertyNames(proxy)
②Object.getOwnPropertySymbols(proxy)
③Object.keys(proxy)
④for...in
循环
返回值:一个数组(数组成员,只能是字符串或 Symbol 值)。该方法返回目标对象所有自身的属性的属性名,而Object.keys(proxy)
的返回结果仅包括目标对象自身的可遍历属性。
看这个例子:
p和target都不包含a,b,c三个属性,但三种拦截方式的返回值都不同(Object.getOwnPropertyNames(proxy)
是return啥,它就返回啥;for…in
和Object.keys(proxy)
则是在return里面找本身确实有的属性返回)
注*:
①使用Object.keys()
方法时,有三类属性会被ownKeys()方法自动过滤,不会返回。
②如果目标对象自身包含不可配置的属性,则该属性必须被ownKeys()方法返回,否则报错;
③如果目标对象是不可扩展的(non-extensible),这时ownKeys()方法返回的数组之中,必须包含原对象的所有属性,且不能包含多余的属性,否则报错。
拦截的操作:Object.getOwnPropertyDescriptor(proxy, propKey)
;
返回值:proxy的propKey属性的描述对象。
拦截的操作:
①Object.defineProperty(proxy, propKey, propDesc)
②Object.defineProperties(proxy, propDescs)
参数释义:
target——目标对象
propKey——目标对象的属性名
propDesc——目标对象属性的描述器
返回值:布尔值。
注*:
①如果目标对象不可扩展(non-extensible),则defineProperty()不能增加目标对象上不存在的属性,否则会报错;
②如果目标对象的某个属性不可写(writable)或不可配置(configurable),则defineProperty()方法不得改变这两个设置。
拦截的操作:Object.preventExtensions(proxy)
;
返回值:必须返回一个布尔值,否则会被自动转为布尔值。
注*:该方法有一个限制,只有目标对象不可扩展时(即Object.isExtensible(proxy)为false),proxy.preventExtensions才能返回true,否则会报错。
拦截的操作:
①Object.prototype.__proto__
②Object.prototype.isPrototypeOf()
③Object.getPrototypeOf()
④Reflect.getPrototypeOf()
⑤instanceof
返回值:一个对象或者null。
注*:如果目标对象不可扩展(non-extensible), getPrototypeOf()
方法必须返回目标对象的原型对象。
拦截的操作:Object.isExtensible(proxy)
;
返回值:只能返回布尔值,否则返回值会被自动转为布尔值。
注*:该方法有一个强限制:返回值必须与目标对象的isExtensible属性保持一致,否则就会抛出错误(即不能谎报军情)。
拦截的操作:Object.setPrototypeOf(proxy, proto)
;
返回值:布尔值。如果目标对象是函数,那么还有两种额外操作可以拦截(见12、13)。
注*:如果目标对象不可扩展(non-extensible),setPrototypeOf()
方法不得改变目标对象的原型。
拦截 Proxy 实例作为函数调用的操作,比如proxy(...args)
、proxy.call(object, ...args)
、proxy.apply(...)
。
参数释义:
target
——目标对象
object
——目标对象的上下文对象(this)
args
——目标对象的参数数组
拦截 Proxy 实例作为构造函数调用的操作,比如new proxy(…args)。
参数释义:
target
:目标对象
args
:构造函数的参数对象
newTarget
:new命令作用的构造函数
返回值:必须是一个对象,否则会报错。
从这开始,不是proxy拦截的操作了!
Proxy.revocable()方法返回一个可取消的 Proxy 实例。
let target = {};
let handler = {};
let {proxy, revoke} = Proxy.revocable(target, handler);
proxy.foo = 123;
proxy.foo // 123
revoke();
proxy.foo // TypeError: Revoked
使用场景:目标对象不允许直接访问,必须通过代理访问,一旦访问结束,就收回代理权,不允许再次访问。
还有以下this指向问题,导致的proxy和target表现不一致的问题,以及proxy在对象拦截方面的潜力,请自行看阮一峰的书或是查资料(Vue3.0数据响应)~
此致