Skip to content

Reflect 元数据

元数据是指使用Reflect(反射)来保存和获取一些用于描述数据的信息.比如方法的参数信息,返回值信息等等

而目前的 ECMA 依然没有支持这种语法,所以在使用的时候需要安装并且引入reflect-metadata

ts
import "reflect-metadata";

注意

(1) 将 Object 对象的一些明显属于语言内部的方法(比如 Object.defineProperty),放到 Reflect 对象上

(2) 修改某些 Object 方法的返回结果,让其变得更合理。

而对于反射元数据来说,就是在 Reflect 上提供了几个方法,用于存取数据相关的信息。

ts
// 为类或者对象上定义元数据
Reflect.defineMetadata(key, value, classObject);
// 为方法定义元数据
Reflect.defineMetadata(key, value, classPrototype, methodName);
// 为属性定义元数据
Reflect.defineMetadata(key, value, classPrototype, propKey);

Reflect.defineMetadata 的前两个参数分别是保存数据的 keyvalue,后面的参数分别是保存的路径,比如上面的后两个例子分别是将数据保存到 classPrototype[methodName]classPrototype[propKey]

在普通对象中定义

在对象上使用:

ts
let obj = {
  name: "小黄瓜",
};

// 定义在obj对象上
Reflect.defineMetadata("objTest1Key", "test1Value", obj);
Reflect.defineMetadata("objTest2Key", "test2Value", obj);

Reflect.getMetadata("objTest1Key", obj); // "test1Value"
// 不存在的定义
console.log(Reflect.getMetadata("test", obj)); // undefined

使用 defineMetadata 来为 obj 对象添加了 keyobjTest1Key 的值 test1Value,然后使用 getMetadata 来进行获取,通过 key 取值,如果 key 不存在,会返回 undefined

如果想要在对象中的属性上定义呢,比如想要定义在 obj 对象的 name 属性上。

ts
// 定义在对象的属性上
Reflect.defineMetadata("propTest1Key", "test1Value", obj, name);
Reflect.defineMetadata("propTest2Key", "test2Value", obj, name);

Reflect.getMetadata("propTest1Key", obj, name); // test1Value
Reflect.getMetadata("propTest2Key", obj, name); // test2Value

通过传递 defineMetadatagetMetadata 方法的参数(依次传递对象名,属性名),来为对象或者属性定义或这获取元数据。

如果想要提前判断元数据存不存在怎么办?当然也为我们贴心的提供了检测方法:

ts
if (Reflect.hasMetadata("objTest3Key", obj)) {
  console.log("objTest3Key存在");
}

使用 hasMetadata 传入元数据的 key,查找是否存在对应元数据,第二个参数依然是存储的位置

在类中定义

在类中有单独的元数据操作方法,可以直接在特定的位置进行存取数据。 比如在类上定义元数据:

ts
@Reflect.metadata("decribe", "黄瓜类")
class People {}

// 获取类上元数据
console.log(Reflect.getMetadata("decribe", People)); // "黄瓜类"

metadata 方法可以直接定义在类上,只接收元数据的 keyvalue,定义的位置是固定的,在类上定义的元数据,定义的位置默认是定义在类上。 所以我们可以通过 getMetadata 获取在 People 类上的元数据。 而如果在实例方法或者实例属性上定义,则与定义装饰器效果类似,分别会定义在 prototype 中的对应的方法或属性上。

ts
class People {
  @Reflect.metadata("decribe", "木星人")
  username = "瓜瓜";
  @Reflect.metadata("importinfo", "吃喝")
  eat() {}
}

// 获取方法上的元数据
console.log(Reflect.getMetadata("importinfo", People.prototype)); // "吃喝"
// 获取属性上的元数据
console.log(Reflect.getMetadata("decribe", People.prototype)); // "木星人"

内置元数据 Key

同样,也存在内置的一些元数据,可以获取一些内置的默认的元数据。当然也是有触发条件的:在当前装饰器修饰前提下执行下面元数据 key

design:paramtypes

a 构造器 : 获得所有参数数据类型组成的数组

ts
@Reflect.metadata("decribe", "都是人")
class People {
  constructor(addr: string, house: boolean) {}
}

Reflect.getMetadata("design:paramtypes", People);
// [ [Function: String], [Function: Boolean] ]

b. 实例方法:获取全部参数的数据类型组成的数组

ts
class People {
  constructor(addr: string, house: boolean) {}
  @Reflect.metadata("importinfo", "吃喝")
  eat(name: string, age: number): boolean {}
}

console.log(Reflect.getMetadata("design:paramtypes", People.prototype, "eat"));
// [ [Function: String], [Function: Number] ]

design:type

a. 实例属性:获取实例属性的数据类型

ts
class People {
  constructor(addr: string, house: boolean) {}

  @Reflect.metadata("decribe", "小黄瓜")
  username: string = "瓜瓜";
}

Reflect.getMetadata("design:type", People.prototype, "username");
// [Function: String]

b. 实例方法:获取实例方法的数据类型

ts
class People {
  constructor(addr: string, house: boolean) {}

  @Reflect.metadata("importinfo", "吃喝")
  eat(name: string, age: number): boolean {}
}

Reflect.getMetadata("design:type", People.prototype, "eat");
// [Function: Function]

design:returntype

  • a:获取实例方法返回值的数据类型
ts
class People {
  constructor(addr: string, house: boolean) {}

  @Reflect.metadata("importinfo", "吃喝")
  eat(name: string, age: number): boolean {}
}

Reflect.getMetadata("design:returntype", People.prototype, "eat");
// [Function: Boolean]

Reflect. getMetadataKeys

ts
class People {
  constructor(addr: string, house: boolean) {}

  @Reflect.metadata("test1", "吃喝")
  @Reflect.metadata("test2", "玩乐")
  eat(name: string, age: number): boolean {}
}

Reflect.getMetadataKeys(People.prototype, "eat");

结果是

bash
[LOG]: ["design:returntype", "design:paramtypes", "design:type", "test2", "test1"]

其中也包含了默认的三个元数据key