代理与反射

代理基础

当目标对象上的进行一些特定的底层操作时,代理允许你拦截这些操作并且覆写

  • 使用Proxy构造函数创建的,两个参数
    • 第一个参数:目标对象
    • 第二个参数:处理程序对象
  • 给目标属性赋值会反映在两个对象上
  • 给代理属性赋值会反映在两个对象上
  • Proxy.prototypeundefined
  • 严格相等可以用来区分代理和目标

主要目的是可以定义捕获器(trap)。捕获器就是在处理程序对象中定义的“基本操作的拦截器”。每个捕获器都对应一种基本操作,可以直接或间接在代理对象上调用。每次在代理对象上调用这些基本操作时,代理可以在这些操作传播到目标对象之前先调用捕获器函数,从而拦截并修改相应的行为。

捕获器不变式

捕获器不变式因方法不同而异,但通常都会防止捕获器定义出现过于反常的行为

例如:如果目标对象有一个不可配置且不可写的数据属性,那么在捕获器返回一个与该属性不同的值时,会抛出TypeError

可撤销代理

  • Proxy也暴露了revocable()方法,这个方法支持撤销代理对象与目标对象的关联
  • 撤销代理的操作是不可逆的
  • 是幂等的
  • 撤销代理之后再调用代理会抛出TypeError

实用反射API

反射方法适用于细粒度的对象控制与操作

  1. 状态标记

    返回称作“状态标记”的布尔值,表示意图执行的操作是否成功。以下反射方法都会提供状态标记

    • Reflect.defineProperty()
    • Reflect.preventExtensions()
    • Reflect.setPrototypeOf()
    • Reflect.set()
    • Reflect.deleteProperty()
  2. 用一等函数替代操作符

    以下反射方法提供只有通过操作符才能完成的操作

    • `Reflect.get()
      • 替代对象属性访问操作符
    • Reflect.set()
      • 替代=赋值操作符
    • Reflect.has()
      • 替代in操作符或with()
    • Reflect.deleteProperty()
      • 替代delete操作符
    • Reflect.construct()
      • 替代new操作符
  3. 安全地应用函数

    • Reflect.apply()

代理另一个代理

可以创建一个代理,通过它去代理另一个代理

代理问题

  1. this问题
  2. 内部槽位

代码捕获器与反射方法

对于在代理对象上执行的任何一种操作,只会有一个捕获处理程序被调用。不会存在重复捕获的情况。

只要在代理上调用,所有捕获器都会拦截它们对应的反射API操作

捕获器 操作 反射 返回值 参数
get()` 获取属性值操作 Reflect.get() 无限制 target、property、receiver
set() 设置属性值 Reflect.set() 布尔值 target、property、value、receiver
has() in操作符 Reflect.has() 布尔值 target、property
defineProperty() 被调用 Reflect.defineProperty() 布尔值 target、property、descriptor
getOwnPropertyDescriptor() 被调用 Reflect.getOwnPropertyDescriptor() 返回对象,属性不存在返回undefined target、property
deleteProperty() delete操作符 Reflect.deleteProperty() 布尔值 target、property
ownKeys() Object.keys()及类似方法中被调用 Reflect.ownKeys() 返回字符串或符号的可枚举对象 target:目标对象
getPrototypeOf() 被调用 Reflect.getPrototypeOf() 返回对象或null target:目标对象
setPrototypeOf() Object.setPrototypeOf()中被调用 Reflect.setPrototypeOf() 布尔值 target、prototype
isExtensible() Object.isExtensible()中被调用 Reflect.isExtensible() 布尔值 target:目标对象
preventExtensions() 被调用 Reflect.preventExtensions() 布尔值 target:目标对象
apply() 调用函数时中被调用 Reflect.apply() 无限制 target、thisArgargumentsList
construct() new操作符中被调用 Reflect.construct() 对象 target、argumentsList、newTarget

代理模式

跟踪属性访问

通过捕获getsethas等操作,可以知道对象属性什么时候被访问、被查询。把实现相应捕获器的某个对象代理放到应用中,可以监控这个对象何时在何处被访问过

隐藏属性

代理的内部实现对外部代码是不可见的,因此要隐藏目标对象上的属性也轻而易举

属性验证

因为所有赋值操作都会触发set()捕获器,所以可以根据所赋的值决定是允许还是拒绝赋值

函数与构造函数参数验证

跟保护和验证对象属性类似,也可对函数和构造函数参数进行审查。比如,可以让函数只接收某种类型的值

数据绑定与可观察对象

通过代理可以把运行时中原本不相关的部分联系到一起。这样就可以实现各种模式,从而让不同的代码互操作