Reflect 元数据
元数据是指使用Reflect
(反射)来保存和获取一些用于描述数据的信息.比如方法的参数信息,返回值信息等等
而目前的 ECMA 依然没有支持这种语法,所以在使用的时候需要安装并且引入reflect-metadata
import "reflect-metadata";
注意
(1) 将 Object 对象的一些明显属于语言内部的方法(比如 Object.defineProperty),放到 Reflect 对象上
(2) 修改某些 Object 方法的返回结果,让其变得更合理。
而对于反射元数据来说,就是在 Reflect 上提供了几个方法,用于存取数据相关的信息。
// 为类或者对象上定义元数据
Reflect.defineMetadata(key, value, classObject);
// 为方法定义元数据
Reflect.defineMetadata(key, value, classPrototype, methodName);
// 为属性定义元数据
Reflect.defineMetadata(key, value, classPrototype, propKey);
Reflect.defineMetadata
的前两个参数分别是保存数据的 key
和 value
,后面的参数分别是保存的路径,比如上面的后两个例子分别是将数据保存到 classPrototype[methodName]
和 classPrototype[propKey]
在普通对象中定义
在对象上使用:
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
对象添加了 key
为 objTest1Key
的值 test1Value
,然后使用 getMetadata
来进行获取,通过 key
取值,如果 key
不存在,会返回 undefined
如果想要在对象中的属性上定义呢,比如想要定义在 obj
对象的 name
属性上。
// 定义在对象的属性上
Reflect.defineMetadata("propTest1Key", "test1Value", obj, name);
Reflect.defineMetadata("propTest2Key", "test2Value", obj, name);
Reflect.getMetadata("propTest1Key", obj, name); // test1Value
Reflect.getMetadata("propTest2Key", obj, name); // test2Value
通过传递 defineMetadata
和 getMetadata
方法的参数(依次传递对象名,属性名),来为对象或者属性定义或这获取元数据。
如果想要提前判断元数据存不存在怎么办?当然也为我们贴心的提供了检测方法:
if (Reflect.hasMetadata("objTest3Key", obj)) {
console.log("objTest3Key存在");
}
使用 hasMetadata
传入元数据的 key
,查找是否存在对应元数据,第二个参数依然是存储的位置
在类中定义
在类中有单独的元数据操作方法,可以直接在特定的位置进行存取数据。 比如在类上定义元数据:
@Reflect.metadata("decribe", "黄瓜类")
class People {}
// 获取类上元数据
console.log(Reflect.getMetadata("decribe", People)); // "黄瓜类"
metadata
方法可以直接定义在类上,只接收元数据的 key
和 value
,定义的位置是固定的,在类上定义的元数据,定义的位置默认是定义在类上。 所以我们可以通过 getMetadata
获取在 People
类上的元数据。 而如果在实例方法或者实例属性上定义,则与定义装饰器效果类似,分别会定义在 prototype
中的对应的方法或属性上。
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 构造器 : 获得所有参数数据类型组成的数组
@Reflect.metadata("decribe", "都是人")
class People {
constructor(addr: string, house: boolean) {}
}
Reflect.getMetadata("design:paramtypes", People);
// [ [Function: String], [Function: Boolean] ]
b. 实例方法:获取全部参数的数据类型组成的数组
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. 实例属性:获取实例属性的数据类型
class People {
constructor(addr: string, house: boolean) {}
@Reflect.metadata("decribe", "小黄瓜")
username: string = "瓜瓜";
}
Reflect.getMetadata("design:type", People.prototype, "username");
// [Function: String]
b. 实例方法:获取实例方法的数据类型
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:获取实例方法返回值的数据类型
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
class People {
constructor(addr: string, house: boolean) {}
@Reflect.metadata("test1", "吃喝")
@Reflect.metadata("test2", "玩乐")
eat(name: string, age: number): boolean {}
}
Reflect.getMetadataKeys(People.prototype, "eat");
结果是
[LOG]: ["design:returntype", "design:paramtypes", "design:type", "test2", "test1"]
其中也包含了默认的三个元数据key