元数据编程 -- Reflect 与 metadata
前一篇文章介绍了 Reflect 基础 API 的内容,使用 Reflect 可以为对象设置属性,这个属性是会被直接添加到对象上的,有时候我们需要添加一种元数据,这种数据是用来描述对象本身,不应该出现在对象上。基于这种需求,有一个关于 metadata 的提案,这个提案在 Reflect 对象上扩展了元数据的能力。元数据是对反射功能的一个补充,在一些框架开发场景下很有用,由于这个功能还没有stable,我们可以引入 reflect-metadata 库来使用元数据能力。
meta-data 提案中包含以下方法:
Reflect.defineMetadata
Reflect.hasMetadata
Reflect.hasOwnMetadata
Reflect.getMetadata
Reflect.getOwnMetadata
Reflect.getMetadataKeys
Reflect.deleteMetadata
从方法名可以看出他们的作用,和常规的数据定义方法名风格是一致的,只是这里定义的是元数据信息元数据不影响程序运行,只能使用元数据相关 API 可以操控元数据。
查看 meta-data 源码可以看到实际上内部是利用一个 WeakMap 来实现的,元数据被存储在一个独立的 WeakMap 中,这样不影响程序本身,只在想获取时去读取即可。这里利用了 map 的数据结构可以接收对象作为 key 的特点。
除了之前提到的方法,Reflect 中还提供了一个装饰器,使用 @Reflect.metadata(metadataKey, metadataValue)
可以直接添加元数据,结合装饰器可以更容易进行元数据编程。使用 @Reflect.metadata
装饰器,我们可以很方便的对一个类的成员方法进行标识:
// Design-time type annotations function Type(type) { return Reflect.metadata("design:type", type); } function ParamTypes(...types) { return Reflect.metadata("design:paramtypes", types); } function ReturnType(type) { return Reflect.metadata("design:returntype", type); } // Decorator application @ParamTypes(String, Number) class C { constructor(text, i) { } @Type(String) get name() { return "text"; } @Type(Function) @ParamTypes(Number, Number) @ReturnType(Number) add(x, y) { return x + y; } } 复制代码
这样通过 Reflect.getMetadata 就可以获取到标识的字段类型、参数类型、返回值类型。
在 typescript 中,编译器可以为我们自动添加上面的三种元数据类型:只需要在 tsconfig 文件中设置 emitDecoratorMetadata 值为 true 即可:
// typescript class Demo { @LogMethod public foo(bar: number) { // do nothing } } // without emitDecoratorMetadata class Demo { foo(bar) { // do nothing } } __decorate([ LogMethod ], Demo.prototype, "foo", null); // with emitDecoratorMetadata class Demo { foo(bar) { // do nothing } } __decorate([ LogMethod, __metadata("design:type", Function), __metadata("design:paramtypes", [Number]), __metadata("design:returntype", void 0) ], Demo.prototype, "foo", null); 复制代码
可以看到当开启 emitDecoratorMetadata 时,在装饰器中会自动添加 design:type、design:paramtypes、design:returntype 三种类型的元数据信息,我们可以通过 Reflect.getMetadata
直接获取相关的类型信息。
元数据编程多用于框架开发,有时需要实现动态加载,或者需要添加一些标识信息,这些场景通常会使用元数据来实现,开源框架 InversifyJS、angular、nest 中都有使用。
最后还是要注意这个提案还没进入标准,目前使用的是 reflect-metadata 库的实现版本,可能以后相关的 API 会有变化。
伪原创工具 SEO网站优化 https://www.237it.com/
作者:丨隋堤倦客丨
链接:https://juejin.cn/post/7035598935225794573