Reflection(Reflect对象 和 Proxy对象)
Reflection(Reflect 和 Proxy)
注意⚠️:Reflection是技术思想,Reflect是指es6实现的具体对象。
Reflect
和 Proxy
是成对设计的,它们一起用于拦截和操控Javascript的低层方法。
为什么这样设计?
-
Reflect提供了一致性:让你可以像调用函数一样使用各种低层操作(而不是语法糖,语法糖包含了太多默认行为是我们不可改变的) 例如:我们可以用
target[prop]
语法糖 获取 某个对象的属性,但是如果遇到getter函数,那么getter函数默认的this就是target对象,但是我们在Proxy代理时,我们希望getter函数执行时内部的this依然指向proxy实例(这样getter内部的访问行为就也可以被拦截到啦~),那我们就可以通过Reflect.get(target, prop, receiver)
第三个参数receiver
修改默认this指向,使其指向proxy。 -
Proxy提供了可控性:让你可以插手这些低层操作,并决定是否、如何处理它们。
-
所以,Reflect是为了支持Proxy拦截机制而设计的。
当我们可以拦截修改JS操作时,其实就是触发了元编程的思想。那么元编程是什么?为什么需要元编程?
元编程
维基百科:元编程是一种编程技术思想,在这种技术中计算机程序可以将其他程序视为自己的一种数据。这意味着一个程序可以被设计用来读取、生成、分析、转换其他程序,甚至在运行时修改自身。
(反射)Reflection 就是一种元编程的实现机制,也可以说是元编程的分支之一,因为Reflection有以下特征:
- 代码能够自我检查(通过Reflect)
- 代码能够自我修改(通过Reflect)
- 可以通过封装、陷阱、拦截来代替原来的对象。(通过Proxy+Reflect)
总之就是让程序在运行时获取或修改自身结构,如类、方法、属性。
举个“不太恰当”的例子🌰:我们可以在函数上记录一些东西,在运行时去影响函数的执行。
Reflect在函数身上记录的数据,可以被称为一种元数据(实际上只是一种普通数据),通过修改元数据 影响程序行为,可以称之为元编程。
注意:(直接用
eatWhich.which
设置修改属性其实也可以做到,但是实际开发中 这两种方式都不建议,都不足够可靠,因为这种所谓的“元数据”会污染对象本身。)
NestJS中的各种装饰器、控制反转Ioc的实现 就是基于
reflect-metadata
一种第三方库做类似的行为:通过Reflect.defineMetadata
给类和属性添加 元数据(真正的元数据 ——给程序看的,而不是给业务逻辑直接使用的)。这些元数据是通过一种“元数据表”存储,而不是直接存到对象属性上,更加可靠。
Reflect
Reflect推出目的是提供一组操作对象的底层方法(类似 Object,但更一致、函数化)。 在 JavaScript 中,有很多底层操作(其实也算一种语法糖),比如:
- 读取属性(
obj.prop
) - 设置属性(
obj.prop = value
) - 删除属性(
delete obj.prop
) - 函数调用(
func.apply(...)
) 这些操作在 ES6 之前没有统一的“底层 API”,只能通过语法来完成。