【Nest全栈之旅】第四章:对Metadata和Reflect一脸懵逼?

第二章我们讲到了装饰器的相关概念,也知道通过装饰器工厂可以扩展装饰器的功能,除此之外,想要实现Nest装饰器效果,还需要了解一些前置知识,即metadataReflect

Reflect对象

很多同学对于这个新的内置对象都熟悉,不就是用来操作对象的属性和方法吗,诸如:

js 复制代码
// set操作
const obj = {}
Reflect.set(obj , 'a', 1)
Reflect.set(obj , 'b', 2)

// get操作
Reflect.get(obj, 'a') // 1

// 判断是否存在某个元素
Reflect.has(obj, 'a') // true
Reflect.has(obj, 'c') // false

除此之外,还有Reflect.definePropertyReflect.ownKeys等在Object对象上能找到的方法。

而这里有同学可能会问,为什么已经有了操作Object的方法,还要有Reflect呢?我补充一下关于Reflect的设计初衷:

  • 为了Proxy准备,Object上有超过20种方法,但是用来操作Proxy显得太杂了,需要专门有个对象来做这件事情。让Proxy对象可以方便地调用对应的Reflect方法,完成默认行为,作为修改行为的基础。
  • 为了减少命令式操作Object,如delete obj.aname in obj,将这些操作变为函数行为,统一ES的编码风格
  • 修改某些Object方法的返回结果,让其变得更合理。比如,Object.defineProperty(obj, name, desc)在无法定义属性时,会抛出一个错误,而Reflect.defineProperty(obj, name, desc)则会返回false

metadata元数据

什么是metadata元数据呢?

简单理解就是用数据来描述数据,什么意思?

举个例子:比如我有一个方法叫做getData,它被挂在methods对象上, 用元数据来描述它可以这样表示。

typescript 复制代码
metadata = {
    // 属性类型
    type: Function,
    // 返回值类型
    returnType: Number,
    // 参数类型
    paramsType: [String, Number]
}

Reflect的api我们都可以在MDN上找到,但是诸如Reflect.getMetadata怎么没有呢?

是因为metadata这个api还在草案阶段,还没有纳入es标准。 rbuckton.github.io/reflect-met...

在这里,我们就可以到看Nest相关api了

给类以及类方法添加元数据:

我们在Nest中测试一下:

PersonService类添加元数据,在Nest启动的时候就可以拿到这个数据

PersonService类中的一个方法findOne添加/获取元数据

到这里,是不是就知道Nest为什么能够在启动的时候就已经把ControllerService等相关依赖收集好并且完成创建对象了!!!

即把上面的{name: 'jmin'}对象替换为对应的依赖对象即可,如下图,Nest就是这样处理的:

我们通过调试方式进入源码来看看Module的实现

可以看到左边就已经获取到了metadata元数据,最终会调用Reflect.defineMetadataAppModule添加元数据,完成依赖收集,最终交给ICO容器统一接管。

看到这里就知道Nest的核心在于metadata,而实际上是通过reflect-metadata这个包进行polyfill的。

其实还有一个问题,实例化构造函数的时候,需要传递具体的参数,但又没有通过装饰器添加,是怎么做到的?比如AppController里面依赖了AppService,在getHello方法中可以直接拿到appService对象的getHello方法。

这就要提到一个typescript的编译选项emitDecoratorMetadata

Nest中默认开启了这个选项,代表着在编译阶段自动给装饰器添加元数据,我们拿一个编译之后的产物来看看。

还是上面的例子,可以看到findOne方法上面额外添加了几个元数据,分别为:

  • design:type: 代表着装饰对象的类型,这里是函数
  • design:paramtypes: 方法参数类型
  • design:returntype: 返回值类型,这个方法没有返回值,为undefined

翻了一下Nest源码,可以看到有定义对应的metadataKey

而在Nest启动的时候,会通过Reflect.getMetadata方法来拿到这个参数进行初始化。

所以这就是为了Nest使用TS的原因,必须依赖它的这个编译选项来实现依赖注入。

总结

好,这一节介绍了关于Reflect对象和metadata元数据在Nest中的承担着不可忽视的角色,而要实现装饰器效果用到了reflect-metadata这个库,它的用途很广,想要在Vue2中使用Composition API也是通过这个库来实现的,原理一样。

最后从这个metadata角度简单了解了Nest的实现原理,在Nest中,它的用途不仅于此,还有权限管理相关的设计也很巧妙的用到了它,后续讲到这块的时候再详细介绍。

相关推荐
excel7 分钟前
webpack 核心编译器 十三 节
前端
腾讯TNTWeb前端团队7 小时前
helux v5 发布了,像pinia一样优雅地管理你的react状态吧
前端·javascript·react.js
Asthenia04129 小时前
Spring AOP 和 Aware:在Bean实例化后-调用BeanPostProcessor开始工作!在初始化方法执行之前!
后端
Asthenia041210 小时前
什么是消除直接左递归 - 编译原理解析
后端
Asthenia041210 小时前
什么是自上而下分析 - 编译原理剖析
后端
Asthenia041210 小时前
什么是语法分析 - 编译原理基础
后端
Asthenia041210 小时前
理解词法分析与LEX:编译器的守门人
后端
uhakadotcom10 小时前
视频直播与视频点播:基础知识与应用场景
后端·面试·架构
范文杰10 小时前
AI 时代如何更高效开发前端组件?21st.dev 给了一种答案
前端·ai编程