代理使用 Proxy 构造函数创建,接收两个参数:目标对象和处理程序对象。传一个简单的对象字面量即可创建一个空代理。
const target = {
id: 'target'
}
const proxy = new Proxy(target, {})
console.log(proxy.id) // target
proxy.id = 'foo'
console.log(proxy.id) // foo
console.log(target.id) // foo
使用代理的主要目的是可以定义捕获器。捕获器就是在处理程序对象中定义的“基本操作的拦截器”。例如定义一个 get() 捕获器,当通过代理对象执行 get 操作时就会触发定义好的捕获器。
const target = {
foo: 'bar'
}
const proxy = new Proxy(target, {
get() {
return 'handler override
}
})
console.log(proxy.foo) // handler override
所有捕获器都可以访问相应的参数,重建被捕获方法的原始行为。例如 get 捕获器可以接收:目标对象、要查询的属性和代理对象三个参数。处理程序对象中所有可以捕获的方法都有对应的反射(Reflect)API方法。例如 get 捕获器:
const target = {
foo: 'bar'
}
const handler = {
get: Reflect.get
}
const proxy = new Proxy(target, handler)
反射API为开发者准备好了样板代码,在此基础上可以用最少的代码修改捕获方法,例如在属性被访问时对返回的值进行修饰:
const target = {
foo: 'bar',
bar: 'qux'
}
const handler = {
get(trapTarget, property, receiver) {
let decoration = ''
if (property === 'foo') {
decoration = '!!!'
}
return Reflect.get(...arguments) + decoration
}
}
const proxy = new Proxy(target, handler)
console.log(proxy.foo) // bar!!!
console.log(proxy.bar) // qux
捕获处理程序的行为必须遵循捕获器不变式,用于防止捕获器定义出现过去反常的行为。
Proxy 暴露了 revocable 方法,支持撤销代理对象与目标对象的关联。撤销代理的操作是不可逆的。
const target = {
foo: 'bar'
}
const handler = {
get() {
return 'intercepted'
}
}
const {proxy, revoke} = Proxy.revocable(target, handler)
console.log(proxy.foo) // intercepted
revoke()
console.log(proxy.foo) // TypeError
代理的问题和不足:
使用代理可以在代码中实现一些有用的编程模式: