JavaScript 中的反射与 Proxy 与 Reflect
JavaScript 中的反射与 Proxy 与 Reflect
反射(Reflect)是指程序运行时访问、检测和修改本身状态和行为的能力。在 ES6 中引入 Reflect 之前,常用 for...in 实现反射,而在引入 Reflect 之后,反射机制在 JavaScript 中得到了更大的延伸与应用。
Reflect
Reflect 是一个全局对象,但与其它全局对象不同,它并不是一个构造函数,所以不能通过 new 来对其进行调用。
而且它所有的属性和方法都是静态的(类似的还有 Math)。
1 | // ** 检测一个对象是否有特定的属性 |
Proxy
Proxy 基于目标对象创建一个代理对象,可以通过代理对象实现对于基本操作的拦截和自定义。
1 | // target 要代理的对象,可以是任何对象,包括函数 |
对 proxy 对象进行操作,如果 handler 中包含了对应的捕捉器,则扑捉器会拦截这个操作,并对其进行处理。如果没有相应的捕捉器,则操作会被转发到被代理的对象。
对被代理对象的操作和相应的捕捉器名称如下:
| 被代理对象的操作 | 捕捉器 |
|---|---|
Object.getPrototypeOf() |
handler.getPrototypeOf() |
Object.setPrototypeOf() |
handler.setPrototypeOf() |
Object.isExtensible() |
handler.isExtensible() |
Object.preventExtensions() |
handler.preventExtensions() |
Object.getOwnPropertyDescriptor() |
handler.getOwnPropertyDescriptor() |
Object.defineProperty() |
handler.defineProperty() |
in |
handler.has() |
| 属性读取 | handler.get() |
| 属性赋值 | handler.set() |
delete |
handler.deleteProperty() |
Object.getOwnPropertyNames() 和 Object.getOwnPropertySymbols() |
handler.ownKeys() |
| 函数调用 | handler.apply() |
new |
handler.construct() |
不变量
为了确保语言功能和行为的一致性,代理对象的捕捉器的返回是有一定限制的,被称为不变量。
set(),如果设置属性值成功,需要返回true,否则返回falsedeleteProperty(),如果属性删除成功,需要返回true,否则返回falsegetPrototypeOf()必须返回被代理对象的原型…
1 | ; |
对于每个可被 Proxy 捕获的内部方法,在 Reflect 中都有一个对应的方法,其名称和参数与 Proxy 捕捉器相同。
所以,我们可以使用 Reflect 来将操作转发给原始对象。
Proxy 的局限
许多内建对象,例如 Map,Set,Date,Promise 等,都使用了所谓的“内部插槽”。
它们类似于属性,但仅限于内部使用,仅用于规范目的。例如,Map 将项目(item)存储在 [[MapData]] 中。内建方法可以直接访问它们,而不通过 [[Get]]/[[Set]] 内部方法。所以 Proxy 无法拦截它们。
1 | let map = new Map(); |
类似的,类的私有属性也是通过内部插槽而不是 get 和 set 方法,采用上面的方法依旧有效
1 | class User { |
JavaScript 中的反射与 Proxy 与 Reflect
https://cocoalei.github.io/blogs/2020/06/18/JavaScript 反射与 Reflect/