代理与反射

102 阅读3分钟

ES6新增的代理和反射为开发者提供了拦截并向基本操作嵌入额外行为的能力。可以给目标对象定义一个关联的代理对象,而这个代理对象可以作为抽象的目标对象来使用,可以在各种操作对目标对象产生影响前加以控制。

代理

代理是目标对象的抽象。代理对象完全独立于目标对象,既能直接操作目标对象,也可以通过代理对象操作,但会绕过代理施加的行为

创建代理

代理通过Proxy构造函数进行实例化。接收两个参数:

  • 目标对象
  • 处理程序

当处理程序为空对象时,就创建了一个空代理。在代理对象上执行的所有操作都会传递到目标对象上

// 目标对象
const target = {
  id:'target'
}

// 处理程序
const handler = {}

// 代理对象
const proxy = new Proxy(target,handler)

console.log(target.id) // target
console.log(proxy.id) // target

proxy.id = 'proxy'
console.log(target.id) // proxy
console.log(proxy.id) // proxy

console.log(proxy == target) // false
console.log(proxy === target) // false

Proxy.prototype是undefined,因此不能使用instanceof

console.log(proxy instanceof Proxy)

1733025287102.jpg

捕获器

捕获器就是定义在处理程序中的零个或多个对基本操作的拦截器。每次在代理对象上操作这些基本操作时,就会在这些操作到达目标对象时,先触发这些捕获器,从而拦截并修改相应的行为

只有在代理对象上执行操作时才会触发捕获器,如果直接在目标对象上执行这些操作依然会产生正常的行为

使用捕获器几乎可以改变所有基本方法的行为,但也不是没有限制。捕获处理程序的行为必须遵循捕获器不变式。捕获器不变式因方法不同而异,但通常都会防止捕获器定义出现过于反常的行为。

// 目标对象
const target = {
  id:'target'
}

// 处理程序
const handler = {
  get(){
    return 'trap get'
  }
}

// 代理对象
const proxy = new Proxy(target,handler)

console.log(target.id) // target
console.log(proxy.id) // trap get

捕获器参数

所有捕获器都可以访问到对应的参数,可以基于这些参数重构被捕获方法的行为

// 目标对象
const target = {
  id:'target'
}

// 处理程序
const handler = {
  get(trapTarget,prop,receiver){
     console.log(trapTarget === target)
     console.log(prop)
     console.log(receiver === proxy)
  }
}

// 代理对象
const proxy = new Proxy(target,handler)

console.log(proxy.id) // true id true

反射API

并不是每个被捕获的方法都像get()这样简单,如果全部对通过自己手动改写就会很麻烦,所以,js提供了一个全局的Reflect对象,可以通过Reflect对象的同名方法轻松进行重建

处理程序对象中所有可以捕获的方法都有对应的反射(Reflect)API方法。这些方法与捕获器拦截的方法具有相同的名称和函数签名,而且也具有与被拦截方法相同的行为

反射API为开发者准备好了样板代码,在此基础上开发者可以用最少的代码修改捕获的方法

// 目标对象
const target = {
  id:'target',
  name:'tony'
}

// 处理程序
const handler = {
  get(trapTarget,prop,receiver){
     if(prop == 'name'){
       return `My name is ${trapTarget[prop]}`
     }else{
       return trapTarget[prop]
     }
  }
}

// 代理对象
const proxy = new Proxy(target,handler)

console.log(proxy.name) // My name is tony

撤销代理

Proxy也暴露了revocable()方法,这个方法支持撤销代理对象与目标对象的关联。撤销代理的操作是不可逆的