ES6新增的代理与反射提供了拦截,并且能够增加其他行为。实际上,就有点像ES6之前的 Object.defineProperty
。用法就是,通过 Proxy
构造函数给目标对象定义一个关联的代理对象,还可以添加捕获器,之后这个代理对象就能够作为抽象的目标对象来使用,而在操作影响到目标对象之前,会被定义的捕获器劫持。
代理的简单使用
const target = {
name: 'clz'
}
const handler = {
get() {
return 'get'
}
}
const proxy = new Proxy(target, handler)
console.log(target.name) // clz
console.log(proxy.name) // get(因为被劫持了)
proxy.name = 'czh'
console.log(target.name) // czh
console.log(proxy.name) // get
所有捕获器都可以访问到相应的参数,基于这些参数可以重新执行被捕获方法的原始行为。 get()
捕获器会接收到目标对象、要查询的属性和代理对象三个参数。
const target = {
name: 'clz',
}
const handler = {
get(trapTarget, property, receiver) {
console.log(trapTarget === target) // true
console.log(property) // name
console.log(receiver === proxy) // true
return trapTarget[property]
}
}
const proxy = new Proxy(target, handler)
console.log(target.name) // clz
console.log(proxy.name) // clz
反射的简单使用
从上面的例子中,我们已经知道可以通过捕获器的参数重建原始行为,但是重建原始行为比较麻烦,特别是不同的捕获器重建的方法不同。这时候就是我们的 Reflect
对象的的闪亮登场了,它封装了原始行为,所以我们只需要调用同名方法就能轻松重建原始行为。
const target = {
name: 'clz',
}
const handler = {
get() {
return Reflect.get(...arguments)
}
// 更简洁版本
// get: Reflect.get
}
const proxy = new Proxy(target, handler)
console.log(target.name) // clz
console.log(proxy.name) // clz
捕获器不变式
捕获器不变式就是指会防止捕获器定义出现不合理的行为。
比如:如果目标对象有一个不可配置且不可写的数据属性,那么在捕获器返回一个与该属性不同的值时,报错
const target = {}
Object.defineProperty(target, 'name', {
writable: false,
value: 'clz'
})
const handler = {
get() {
// return 'clz'
return 'czh' // 如果属性为不可写,但是返回了与该属性不同的值的话,就会报错
}
}
const proxy = new Proxy(target, handler)
console.log(proxy.name)
可撤销代理
代理是可以撤销的。通过调用Proxy.revocable()
方法,这个方法返回的对象中,有代理对象 proxy
,以及撤销代理的方法 revoke
。撤销代理的操作是不可逆的。撤销代理之后再调用代理会报错
const target = {
name: 'clz'
}
const handler = {
get() {
return 'hello'
}
}
const { proxy, revoke } = Proxy.revocable(target, handler)
console.log(proxy.name)
console.log(target.name)
revoke()
console.log(proxy.name) // TypeError: Cannot perform 'get' on a proxy that has been revoked
代理另一个代理
我们的代理不一定只是能代理普通的对象,还能够代理另一个代理,做法和代理普通的对象一样。
const target = {
name: 'clz'
}
const firstProxy = new Proxy(target, {
get() {
console.log('第一次代理')
return Reflect.get(...arguments)
}
})
const secondProxy = new Proxy(firstProxy, {
get() {
console.log('第二次代理')
return Reflect.get(...arguments)
}
})
console.log(secondProxy.name)
通过代理去影响目标对象会按最后的代理一次回到最初的代理的顺序。