前言

前文《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 中会经常用到装饰器和反射元数据。