JavaScript高级程序设计第4版-第 9 章

57 阅读3分钟

第九章,代理与反射

注意事项

  1. Proxy.prototype === undefined
  2. 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]

...