第二章我们讲到了装饰器的相关概念,也知道通过
装饰器工厂
可以扩展装饰器的功能,除此之外,想要实现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
中,它的用途不仅于此,还有权限管理相关的设计也很巧妙的用到了它,后续讲到这块的时候再详细介绍。