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

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

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.defineProperty、Reflect.ownKeys等在Object对象上能找到的方法。

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

  • 为了Proxy准备,Object上有超过20种方法,但是用来操作Proxy显得太杂了,需要专门有个对象来做这件事情。让Proxy对象可以方便地调用对应的Reflect方法,完成默认行为,作为修改行为的基础。
  • 为了减少命令式操作Object,如delete obj.a、name 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为什么能够在启动的时候就已经把Controller、Service等相关依赖收集好并且完成创建对象了!!!

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

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

可以看到左边就已经获取到了metadata元数据,最终会调用Reflect.defineMetadata给AppModule添加元数据,完成依赖收集,最终交给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中,它的用途不仅于此,还有权限管理相关的设计也很巧妙的用到了它,后续讲到这块的时候再详细介绍。

相关推荐
架构技术专栏2 小时前
Agent 工具调用:从工具声明到执行反馈
前端·人工智能·后端
别动我齐刘海3 小时前
C++ 工程化学习
java·linux·前端·c++·学习·机器学习·机器人
码事漫谈3 小时前
你让AI干的活,它可能只干了十分之一——而你根本看不出来
后端
Wx-bishekaifayuan3 小时前
express皖美特色农产品网售系统53118-计算机课程设计、毕业设计
人工智能·spring boot·后端·python·课程设计·express·旅游
志尊宝4 小时前
Vue3 零基础每日笔记(071):暗黑模式与主题切换——CSS 变量 + Element Plus 暗黑方案
前端·css·笔记·vue·html·前端开发·软件开发
Wx-bishekaifayuan4 小时前
springboot汉中旅游服务平台41442-计算机课程设计、毕业设计
spring boot·后端·python·django·课程设计·express·旅游
WeiXin_DZbishe4 小时前
基于springboot城市公交实时运营与调度系统-计算机毕设 附源码【课程设计】66173
java·vue.js·spring boot·后端·python·php·课程设计
刘发财4 小时前
前端如何生成10000页PDF文件?
前端·javascript·开源
WeiXin_DZbishe4 小时前
基于Springboot+AI的kuall音乐广场的设计与实现-计算机毕设 附源码【课程设计】12715
vue.js·人工智能·spring boot·后端·python·php·课程设计
A接拉起0075 小时前
2026 年 JavaScript 开发者需要了解的知识
前端·javascript·后端