前言
前文《ES6 拾遗:理解 Reflect 反射对象 》介绍了 ES 标准的 Reflect 反射对象,它提供了13个处理对象的静态方法,可以认为 Reflect 对象就是用来专门操作对象的。本文继续介绍另一个和反射有关的概念——反射元数据。
Metadata 元数据
元数据在开发中是一个很常见的概念,意思是描述数据的数据(Data that describes other data)。比如拍了一张照片,我们只关心画面好不好看,但是摄影师会关注曝光,光圈,白平衡等信息,这些就是照片的元数据。
开发中元数据通常是和装饰器联系在一起使用的,在 Nestjs 中大量使用了装饰器,也会通过元数据给类或方法注入一些信息,然后通过反射器来获取。
Reflect Metadata 反射元数据
反射元数据是 ECMAScript 标准的一个提案,但和装饰器提案一样,命途坎坷,至今未被纳入正式标准。ES6 的Reflect 对象的方法全部是用来操作对象的,没有用来操作元数据的方法。反射元数据是对 Reflect 反射对象的补充,专门用来设置和读取元数据。
由于尚未实现这个标准,社区有一个 reflect-metadata 模块,它为 Reflect 对象扩展了一些方法,比如:
- Reflect.metadata:定义元数据的装饰器
- Reflect.defineMetadata:定义元数据
- Reflect.hasMetadata:判断元数据是否存在,会沿着原型链查找
- Reflect.hasOwnMetadata:判断 target 自身是否存在元数据
- Reflect.getMetadata:获取元数据,会沿着原型链查找
- Reflect.getOwnMetadata:只从 target 获取元数据
- Reflect.getMetadataKeys:获取所有元数据的 Key
- Reflect.getOwnMetadataKeys:只获取 target 身上的 key
- Reflect.deleteMetadata:删除元数据
使用 reflect-metadata
创建一个新项目:
$ mkdir metadata
$ cd metadata
$ npm init
$ npm install typescript reflect-metadata
初始化 TS 配置文件:
$ npx tsc --init
然后将 tsconfig.json
中的两个和装饰器和元数据有关的选项打开:
{
"compilerOptions": {
"experimentalDecorators": true,
"emitDecoratorMetadata": true
}
}
然后使用 reflect-metadata 模块去定义元数据:
import "reflect-metadata";
@Reflect.metadata("Class", "animal metadata")
class Animal {
@Reflect.metadata("Prototype Method", "eat metadata")
eat() {
console.log('eat')
}
}
上面代码中定义了一个 Animal 类,它具有一个 eat
方法,然后使用 @Reflect.metadata()
装饰器向 Animal
类和 eat
方法分别设置了元数据。
使用下面的代码来获取元数据有关的信息:
// 获取类的元数据的 key
console.log(Reflect.getMetadataKeys(Animal));
// 获取类的原型对象的元数据的 key, 也就是设置在类方法上的 key
console.log(Reflect.getMetadataKeys(Animal.prototype));
// 根据 key 获取类的元数据
console.log(Reflect.getMetadata("Class", Animal));
// 根据 key 获取类方法的元数据
console.log(Reflect.getMetadata("Prototype Method", Animal, "eat"));
除了使用 @Reflect.metadata
装饰器直接在类上设置元数据,还可以使用 Reflect.defineMetadata
方法去设置:
Reflect.defineMetadata("Class", "animal metadata", Animal);
Reflect.defineMetadata("Prototype Method", "eat metadata", Animal.prototype, "eat");
总结
本文介绍了反射元数据 Reflect Metadata,它用来处理元数据。由于尚未实现正式标准,现在一般使用社区的实现 reflect-metadata 模块。在 Nestjs 中会经常用到装饰器和反射元数据。