第九章,代理与反射
注意事项
- Proxy.prototype === undefined
- Proxy为完全断层更新,不可转换成低版本代码
基本写法及其概念
基础写法
// 捕获对象(trapTarget
const target = {
name: 'xz'
}
// 捕获器(trap
const handler = {
// 捕获对象 属性名 触发器(一般为代理对象
get(trapTarget, property, receiver) {
return trapTarget[property]
}
}
// 代理对象
const proxy = new Proxy(target, handler)
proxy.name // xz
之所以说 receiver 一般是代理对象,是因为以下情况。
const target = {}
const handler = {
get(trapTarget, property, receiver) {
receiver === obj // true
return trapTarget[property]
}
}
// 代理对象
const proxy = new Proxy(target, handler)
const obj = {}
Object.setPrototypeOf(obj, proxy)
obj.name
可撤销代理
const {proxy, revoke} = Proxy.revocable(target, handler)
proxy.name
revoke()
proxy.name // Uncaught TypeError TypeError: Cannot perform 'get' on a proxy that has been revoked
反射
Reflect 是全局对象,一般对象上的方法他也有,不同的是其他方法报错,他不报错,取而代之的是他会返回状态标记,代表此次操作执行是否成功
const obj = {}
Object.defineProperty(obj, 'name', {
value: 'xz',
configurable:false
})
Object.defineProperty(obj, 'name',{
configurable:true,
}) // TypeError
Reflect.defineProperty(obj, 'name', {
configurable:true
}) // 同样不能修改,但不报错
常用的有 set,get,has,deleteProperty,ownKeys
proxy this 指向问题
const target = {
getThis(){
console.log(this)
}
}
const proxy = new Proxy(target, {})
target.getThis() // target
proxy.getThis() // proxy
可以 return trapTarget[property].call(trapTarget)
捕获不变式
对于捕获器来说,每个处理函数都有不同的返回规则,这个规则就叫捕获不变式。各种常用处理函数的捕获不变式规则如下:
const target = {}
const handler = {
get(trapTarget, property, receiver) {
// 如果 target.property 不可写且不可配置,则处理程序返回的值必须与 target.property 匹配。
// 如果 target.property 不可配置且[[Get]]特性为 undefined,处理程序的返回值也必须是 undefined。
return Reflect.get(...arguments)
},
set(trapTarget, property, value, receiver) {
// 如果 target.property 不可写且不可配置,则不能修改目标属性的值。
// 如果 target.property 不可配置且[[Set]]特性为 undefined,则不能修改目标属性的值。
// 在严格模式下,处理程序中返回 false 会抛出 TypeError。
},
has(trapTarget, property) {
// 如果 target.property 存在且不可配置,则处理程序必须返回 true。
// 如果 target.property 存在且目标对象不可扩展,则处理程序必须返回 true。
},
defineProperty(trapTarget, property, descriptor) {
// 如果目标对象不可扩展,则无法定义属性。
// 如果目标对象有一个可配置的属性,则不能添加同名的不可配置属性。
// 如果目标对象有一个不可配置的属性,则不能添加同名的可配置属性。
},
getOwnPropertyDescriptor(trapTarget, property) {},
deleteProperty(trapTarget, property) {},
ownKeys(trapTarget) {},
getPrototypeOf(trapTarget) {},
setPrototypeOf(trapTarget, prototype) {},
isExtensible(trapTarget) {},
construct(trapTarget,args,bingThis){}
}
const proxy = new Proxy(target, handler)
我并没有写完,用到再去查即可,因为在正常使用的对象情况下,大部分都如常规预料的一般。不一样的一般都是不可配置,不可写,不可扩展所引起的。
书中有这样一句话:对于在代理对象上执行的任何一种操作,只会有一个捕获处理程序被调用,不会存在重复捕获的情况。
大部分确实如此,但是经实验,赋值操作会触发 set 和 defineProperty 两种处理函数
另外,书中这部分内容我在写代码测试时,多处与预期结果不一致 TODO
应用
数据劫持:上述代码
隐藏属性
对外暴露代理对象,那么在通过代理对象访问需要隐藏的属性时不返回即可
handle() {
has(trapTarget, property) {
if(property === 'password') return undefined
}
}
属性验证
赋值时,根据合法类型才给予通过
函数与构造函数参数验证
function foo(...args) {
console.log(...args)
}
const proxy = new Proxy(foo, {
apply(trapTarget, thisArg, argumentsList) {
// do something
}
})
const obj = {
name: 'xz',
say: proxy
}
obj.say(1, 2, 3) // true obj [1, 2, 3]
...