代理与反射
代理基础
当目标对象上的进行一些特定的底层操作时,代理允许你拦截这些操作并且覆写它
- 使用
Proxy
构造函数创建的,两个参数- 第一个参数:目标对象
- 第二个参数:处理程序对象
- 给目标属性赋值会反映在两个对象上
- 给代理属性赋值会反映在两个对象上
Proxy.prototype
是undefined
- 严格相等可以用来区分代理和目标
主要目的是可以定义捕获器(trap
)。捕获器就是在处理程序对象中定义的“基本操作的拦截器”。每个捕获器都对应一种基本操作,可以直接或间接在代理对象上调用。每次在代理对象上调用这些基本操作时,代理可以在这些操作传播到目标对象之前先调用捕获器函数,从而拦截并修改相应的行为。
捕获器不变式
捕获器不变式因方法不同而异,但通常都会防止捕获器定义出现过于反常的行为
例如:如果目标对象有一个不可配置且不可写的数据属性,那么在捕获器返回一个与该属性不同的值时,会抛出TypeError
可撤销代理
Proxy
也暴露了revocable()
方法,这个方法支持撤销代理对象与目标对象的关联- 撤销代理的操作是不可逆的
- 是幂等的
- 撤销代理之后再调用代理会抛出
TypeError
实用反射API
反射方法适用于细粒度的对象控制与操作
状态标记
返回称作“状态标记”的布尔值,表示意图执行的操作是否成功。以下反射方法都会提供状态标记
Reflect.defineProperty()
Reflect.preventExtensions()
Reflect.setPrototypeOf()
Reflect.set()
Reflect.deleteProperty()
用一等函数替代操作符
以下反射方法提供只有通过操作符才能完成的操作
- `Reflect.get()
- 替代对象属性访问操作符
Reflect.set()
- 替代=赋值操作符
Reflect.has()
- 替代in操作符或
with()
- 替代in操作符或
Reflect.deleteProperty()
- 替代
delete
操作符
- 替代
Reflect.construct()
- 替代
new
操作符
- 替代
- `Reflect.get()
安全地应用函数
Reflect.apply()
代理另一个代理
可以创建一个代理,通过它去代理另一个代理
代理问题
this
问题- 内部槽位
代码捕获器与反射方法
对于在代理对象上执行的任何一种操作,只会有一个捕获处理程序被调用。不会存在重复捕获的情况。
只要在代理上调用,所有捕获器都会拦截它们对应的反射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 |
代理模式
跟踪属性访问
通过捕获get
、set
和has
等操作,可以知道对象属性什么时候被访问、被查询。把实现相应捕获器的某个对象代理放到应用中,可以监控这个对象何时在何处被访问过
隐藏属性
代理的内部实现对外部代码是不可见的,因此要隐藏目标对象上的属性也轻而易举
属性验证
因为所有赋值操作都会触发set()捕获器,所以可以根据所赋的值决定是允许还是拒绝赋值
函数与构造函数参数验证
跟保护和验证对象属性类似,也可对函数和构造函数参数进行审查。比如,可以让函数只接收某种类型的值
数据绑定与可观察对象
通过代理可以把运行时中原本不相关的部分联系到一起。这样就可以实现各种模式,从而让不同的代码互操作