WHAT – reflect-metadata

作者 : admin 本文共4699个字,预计阅读时间需要12分钟 发布时间: 2024-06-5 共2人阅读

目录

  • 一、介绍
    • 1.1 产生背景
    • 1.2 目标
    • 1.2 主要功能
  • 二、对象的元数据
  • 三、场景:依赖注入
  • 四、场景:ORM(对象关系映射)

一、介绍

reflect-metadata 是一个JavaScript库,用于在运行时获取和操作JavaScript对象的元数据。它提供了一组装饰器(decorators)和一些辅助函数,用于在JavaScript类和成员上添加、读取和修改元数据。

1.1 产生背景

  • Decorators add the ability to augment a class and its members as the class is defined, through a declarative syntax. 装饰器通过声明式语法,在定义类时为类及其成员提供了扩展的能力。
  • Traceur attaches annotations to a static property on the class. Traceur 将注解附加到类的静态属性上。
  • Languages like C# (.NET), and Java support attributes or annotations that add metadata to types, along with a reflective API for reading metadata. C# (.NET)Java 等编程语言支持属性或注解,可以向类型添加元数据,并提供反射API来读取元数据。

1.2 目标

  • A number of use cases (Composition/Dependency Injection, Runtime Type Assertions, Reflection/Mirroring, Testing) want the ability to add additional metadata to a class in a consistent manner. 许多用例(组合/依赖注入、运行时类型断言、反射/镜像、测试)希望能够以一致的方式向类添加额外的元数据。
  • A consistent approach is needed for various tools and libraries to be able to reason over metadata. 需要一种一致的方法,使得各种工具和库能够对元数据进行推理。
  • Metadata-producing decorators (nee. “Annotations”) need to be generally composable with mutating decorators. 生成元数据的装饰器(原名“注解”)需要能够与修改装饰器进行一般化的组合。
  • Metadata should be available not only on an object but also through a Proxy, with related traps. 元数据不仅应该在对象上可用,还应该通过代理对象以及相关的 traps 进行访问。
  • Defining new metadata-producing decorators should not be arduous or over-complex for a developer. 对于开发人员来说,定义新的生成元数据的装饰器不应该是繁琐或过于复杂的。
  • Metadata should be consistent with other language and runtime features of ECMAScript. 元数据应该与ECMAScript的其他语言和运行时特性保持一致。

1.2 主要功能

  1. 添加元数据:使用装饰器将元数据添加到类、方法、属性等上。例如,可以使用@Reflect.metadata()装饰器将元数据附加到一个类或方法上。
class C {
  @Reflect.metadata(metadataKey, metadataValue)
  method() {
  }
}

// equals to
// 自定义一个装饰器来添加元数据
function MyMetadata(key: string, value: any) {
  return function(target: any, propertyKey: string) {
    Reflect.defineMetadata(key, value, target, propertyKey);
  };
}
  1. 读取元数据:使用Reflect.getMetadata()函数读取已添加的元数据。可以通过提供元数据的键来获取相应的元数据值。
let obj = new C();
let metadataValue = Reflect.getMetadata(metadataKey, obj, "method");
  1. 修改元数据:使用Reflect.defineMetadata()函数修改已添加的元数据。可以通过提供元数据的键和新的元数据值来修改元数据。
Reflect.defineMetadata(metadataKey, metadataValue, C.prototype, "method");

完整实例:

import 'reflect-metadata';

// 定义一个类,并在其中的方法上添加元数据
class MyClass {
  @Reflect.metadata('type', 'user')
  @Reflect.metadata('name', 'John')
  myMethod() {}
}

// 读取元数据
const type = Reflect.getMetadata('type', MyClass.prototype, 'myMethod');
const name = Reflect.getMetadata('name', MyClass.prototype, 'myMethod');

console.log(type); // 'user'
console.log(name); // 'John'

通过使用reflect-metadata,可以在运行时动态地为对象添加附加信息,这对于一些需要在运行时进行元数据操作的场景非常有用,例如在依赖注入、==ORM(对象关系映射)==等方面。

需要注意的是,reflect-metadata是一个第三方库,需要在项目中引入并使用。它在TypeScript中广泛使用,但也可以用于纯JavaScript项目中。

二、对象的元数据

在JavaScript中,元数据是指与对象相关的数据,可以包含关于对象的类型、属性、方法等的信息。元数据不是对象的属性或方法,而是与对象相关联的数据。通过使用reflect-metadata库,可以在运行时动态地操作和获取对象的元数据,而不需要依赖于静态的类型系统。

元数据在JavaScript中通常使用对象属性的方式来实现。例如,可以使用以下方式添加和读取一个对象的元数据:

// 添加元数据
myObject.__metadata__ = {type: 'user', name: 'John'};

// 读取元数据
console.log(myObject.__metadata__.type); // 'user'
console.log(myObject.__metadata__.name); // 'John'

在ES6及更高版本中,可以使用Reflect对象的metadata方法来获取和设置对象的元数据。例如:

// 添加元数据
Reflect.defineMetadata('type', 'user', myObject);
Reflect.defineMetadata('name', 'John', myObject);

// 读取元数据
console.log(Reflect.getMetadata('type', myObject)); // 'user'
console.log(Reflect.getMetadata('name', myObject)); // 'John'

需要注意的是,元数据不是JavaScript标准的一部分,而是一个非正式的概念。

因此,需要使用第三方库(如reflect-metadata)来实现元数据的操作。同时,由于元数据不是JavaScript标准的一部分,因此它的使用方法和实现方式可能因库而异。

三、场景:依赖注入

import 'reflect-metadata';

class Foo {}
class Bar {}

class MyService {
  constructor(
    @Inject('foo') private foo: Foo,
    @Inject('bar') private bar: Bar,
  ) {}
}

function Inject(key: string) {
  return function(target: any, propertyKey: string, parameterIndex: number) {
    const metadata = Reflect.getMetadata('design:paramtypes', target, propertyKey);
    metadata[parameterIndex] = { key };
    Reflect.defineMetadata('design:paramtypes', metadata, target, propertyKey);
  };
}

const myService = new MyService(new Foo(), new Bar());
const metadata = Reflect.getMetadata('design:paramtypes', myService, undefined);
console.log(metadata); // [ { key: 'foo' }, { key: 'bar' } ]

上面的代码中,我们定义了一个 MyService 类,并在其构造函数中使用 @Inject() 装饰器添加了元数据。在 Inject() 装饰器中,我们使用 Reflect.getMetadata() 获取到函数参数的元数据,然后修改其中的元素,将 key 信息添加到其中,最后使用 Reflect.defineMetadata() 将修改后的元数据重新设置到函数参数上。最终,我们使用 Reflect.getMetadata() 来读取 MyService 实例的元数据,并打印出结果。

四、场景:ORM(对象关系映射)

import 'reflect-metadata';

class User {
  @Column()
  id: number;
  @Column()
  name: string;
  @Column()
  age: number;
}

function Column() {
  return function(target: any, propertyKey: string) {
    const metadata = {
      type: Reflect.getMetadata('design:type', target, propertyKey),
      name: propertyKey,
    };
    Reflect.defineMetadata('column', metadata, target, propertyKey);
  };
}

const metadata = Reflect.getMetadata('column', User.prototype, 'id');
console.log(metadata); // { type: Function, name: 'id' }

上面的代码中,我们定义了一个 User 类,并在其属性上使用 @Column() 装饰器添加了元数据。在 Column() 装饰器中,我们使用 Reflect.getMetadata() 获取到属性类型的元数据以及属性名称,然后将其封装为一个对象,并使用 Reflect.defineMetadata() 将其设置到属性上。最终,我们使用 Reflect.getMetadata() 来读取 User 类的 id 属性的元数据,并打印出结果。

本站无任何商业行为
个人在线分享 » WHAT – reflect-metadata
E-->