一、JavaScript 原生元数据
1. 函数/对象的固有元数据
function example(a, b) {}
console.log(example.name); // "example"(函数名)
console.log(example.length); // 2(参数个数)
2. Symbol 类型元数据
ES6 的 Symbol
可作为唯一元数据标识:
const META = Symbol('metadata');
const obj = {
[META]: { version: 1.0, author: 'Alice' }
};
console.log(obj[META]); // { version: 1.0, author: 'Alice' }
二、TypeScript 元数据(需配合装饰器)
1. 编译配置
需启用 tsconfig.json
:
{
"compilerOptions": {
"experimentalDecorators": true,
"emitDecoratorMetadata": true
}
}
2. 反射元数据 API
使用 reflect-metadata
库:
import 'reflect-metadata';
class User {
@Reflect.metadata('role', 'admin')
login() {}
}
const meta = Reflect.getMetadata('role', User.prototype, 'login');
console.log(meta); // 'admin'
三、装饰器中的元数据
1. 类装饰器元数据
function Injectable() {
return (target: Function) => {
Reflect.defineMetadata('service', true, target);
};
}
@Injectable()
class DataService {}
const isService = Reflect.getMetadata('service', DataService);
console.log(isService); // true
2. 参数装饰器元数据
function ParamType(type: any) {
return (target: Object, key: string, index: number) => {
Reflect.defineMetadata(`param_${key}_${index}`, type, target);
};
}
class Controller {
getUser(@ParamType(String) id: string) {}
}
const paramType = Reflect.getMetadata('param_getUser_0', Controller.prototype);
console.log(paramType); // String
四、元数据应用场景
1. 依赖注入框架
// 伪代码示例
class Container {
resolve(target: Function) {
const dependencies = Reflect.getMetadata('design:paramtypes', target);
// 根据参数类型实例化依赖
}
}
2. API 文档生成
通过解析元数据自动生成 Swagger 文档:
@ApiController('/users')
class UserController {
@Get('/:id')
@ApiResponse({ status: 200, type: User })
getUser(@Path() id: string) {}
}
3. 序列化/反序列化
class Serializer {
serialize(obj: Object) {
const metadata = Reflect.getMetadata('serialization', obj.constructor);
// 根据元数据规则转换对象
}
}
五、高级技巧
1. 元数据继承
function InheritMetadata() {
return (target: Function) => {
const parentMeta = Reflect.getMetadata('custom', Object.getPrototypeOf(target));
Reflect.defineMetadata('custom', parentMeta, target);
};
}
2. 元数据验证
function Validate() {
return (target: Object, key: string) => {
const type = Reflect.getMetadata('design:type', target, key);
// 根据类型进行运行时验证
};
}
六、注意事项
- 性能影响:元数据操作会带来运行时开销
- 类型擦除:TypeScript 元数据在编译后可能丢失类型细节
- 兼容性:需配合 Babel 插件或 TypeScript 编译
七、推荐工具库
reflect-metadata
:官方元数据反射库class-validator
:基于装饰器的验证库typedi
:轻量级依赖注入容器routing-controllers
:基于装饰器的路由框架
通过合理使用元数据,可以实现声明式编程、减少样板代码,并提升代码的可维护性。