ES6新增的代理与反射为开发者提供了拦截并基本操作嵌入额外行为的能力.
可以给目标对象定义一个关联的代理对象,而这个代理对象可以作为抽象的目标对象来使用
9.1 代理基础
代理类似C++的指针,可以用作目标对象的替身,但是又完全独立于目标对象
9.1.1 创建空代理
使用Proxy构造函数创建代理
接收两个参数:目标对象和处理程序对象
const target = {
id: 'target'
}
const handler = {}
// 创建代理
const proxy = new Proxy(target,handler)
//实例和代理指向的是同一个值
console.log(target.id);
console.log(proxy.id);
9.1.2 定义捕获器
使用代理的主要目的是可以定义捕获器(trap)
捕获器可以暂停程序流,执行一段子程序后再返回原始程序流
9.1.3 捕获器参数和反射API
所有捕获器都可以访问相应参数,基于参数可以重建捕获方法的原始行为
处理对象中所有可以捕获的方法都具有相应的反射(Reflect)方法
const target = {
id: 'target'
}
const handler = {
get(){
return Reflect.get(...arguments)
}
// 简写
get :Reflect.get
}
9.1.4 捕获器不变式
捕获器不变式会防止捕获器定义出现过于反常的行为
9.1.5 可撤销代理
Proxy.revocable()会撤销代理对象和目标对象之间的关联,撤销的操作是不可逆的
撤销函数(revoke())是幂等的,调用多少次都一样
撤销之后再调用代理会报错
// 撤销代理
const {proxy,revoke} = Proxy.revocable(target,handler)
revoke()
9.1.6 实用反射API
某些情况下应该优先使用反射API
1.反射API与对象API
反射API不局限于捕获处理程序
大多数反射API在Object类型有对应的方法
2.状态标记
很多状态方法返回称作"状态标记"的布尔值
可以用它去找出错误
const o = {}
if (Reflect.defineProperty(o, 'foo', { value: 'bar' })) {
console.log('success')
} else {
console.log('failure')
}
3.用一等函数代替操作符
4.安全的使用函数
9.1.7 代理另一个代理
可以创建一个代理,让它去代理另一个代理.这样就可以在一个目标上构建多层拦截网
9.1.8 代理的问题
1.this
如果目标对象依赖对象标识,那么有可能会出现问题
2.代理与内部槽位
有些内置类型(如Date),在代理调用某些方法时会出错
9.2 代理捕获器与反射方法
代理可以捕获13中不同的基本操作
1.get()
返回值:无限制
拦截的操作:
proxy.property
proxy[property]
Object.create(proxy)[property]
Reflect.get(proxy,property,receiver)
捕获器处理程序参数
target:目标对象
property:引用目标对象身上的字符串键属性
receiver:代理对象或继承代理对象的对象
2.set()
返回值:成功true,失败false,严格模式下TypeError
拦截的操作:
proxy.property = value
proxy[property] = value
Object.create(proxy)[property] = value
Reflect.get(proxy,property,value,receiver)
捕获器处理程序参数
target:目标对象
property:引用目标对象身上的字符串键属性
value:要付给属性的值
receiver:接受最初赋值的对象
3.has()
返回值:必须是布尔值,表示属性是否存在
拦截的操作:
property in proxy
property in Object.create(proxy)
with(proxy) {(property);}
Reflect.has(proxy,property)
捕获器处理程序参数:
target:目标对象
property:引用目标对象身上的字符串键属性
4.defineProperty()
返回值:必须是布尔值,表示属性是否成功定义
拦截的操作:
Object.defineProperties(proxy,property,descriptor)
Reflect.defineProperties(proxy,property,descriptor)
捕获器处理程序参数
target:目标对象
property:引用目标对象身上的字符串键属性
descriptor:包含可选的enumerable,configurable,writable,value,get,set定义的对象
5.getOwnPropertyDescriptor()
返回值:对象或undefined
拦截的操作:
Object.getOwnPropertyDescriptor(proxy,property)
Reflect.getOwnPropertyDescriptor(proxy,property)
捕获器处理程序参数
target:目标对象
property:引用目标对象身上的字符串键属性
6.deleteProperty()
返回值:布尔值,表示是否删除成功
拦截的操作:
delete proxy.property
delete proxy[property]
Reflect.deleteProperty(proxy,property)
捕获器处理程序参数
target:目标对象
property:引用目标对象身上的字符串键属性
7.ownKeys()
返回值:必须返回包含字符串或符号的可枚举对象
拦截的操作:
Object.getOwnPropertyNames(proxy)
Object.getOwnPropertySymbols(proxy)
Object.keys(proxy)
Reflect.ownKeys(proxy)
捕获器处理程序参数
target:目标对象
8.getPrototypeOf()
返回值:必须返回对象或null
拦截的操作:
Object.getPrototypeOf(proxy)
Reflect.getPrototypeOf(proxy)
proxy.__proto__
Object.prototype.isPrototypeOf(proxy)
proxy instanceof object
捕获器处理程序参数
target:目标对象
9.setPrototypeOf()
返回值:返回布尔值,表示原型赋值是否成功
拦截的操作:
Object.setPrototypeOf(proxy)
Reflect.setPrototypeOf(proxy)
捕获器处理程序参数
target:目标对象
prototype:target的替代原型,如果是顶级原型则为null
10.isExtensible()
返回值:返回布尔值,表示原型赋值是否可拓展
拦截的操作:
Object.isExtensible(proxy)
Reflect.isExtensible(proxy)
捕获器处理程序参数
target:目标对象
11.preventExtensions()
返回值:返回布尔值,表示原型赋值是否已经不可拓展
拦截的操作:
Object.preventExtensions(proxy)
Reflect.preventExtensions(proxy)
捕获器处理程序参数
target:目标对象
12.apply()
返回值:无限制
拦截的操作:
proxy(...argumentsList)
Function.prototype.apply(thisArg,argumentsList)
Function.prototype.call(thisArg,argumentsList)
Reflect.apply(target,thisArg,argumentsList)
捕获器处理程序参数
target:目标对象
thisArg:调用函数是的this参数
argumentsList:调用函数时的参数列表
12.construct()
返回值:必须返回一个对象
拦截的操作:
new proxy(...argumentsList)
Reflect.construct(target,argumentsList,newTarget)
捕获器处理程序参数
target:目标构造函数
argumentsList:传给目标构造函数的参数列表
newTarget:最初被调用的构造函数
9.3 代理模式
1.跟踪访问属性
通过get,set,has等操作,可以知道对象属性什么时候被查询,访问
2.隐藏属性
代理的内部实现对外部代码是不可见的
3.属性验证
所有的赋值操作都会触发set()捕获器,所以可以柑橘所赋的值决定允许或拒绝
.4.函数与构造函数参数验证
可以对参数进行审查,让其只接受某种类型的参数
5.数据绑定与可观察对象
可以把不相关的部分集中到一起