最近在看NestJS
,发现控制器成员函数参数中可以使用@Query
、@Body
等函数参数装饰器来获取响应的请求数据,如下:
class CatsController {
@Get()
findAll(@Query() query, @Req() request: Request): string {
return request.url;
}
}
这样写优点很显然,我可以获取我想要的数据,另外不用在乎参数的顺序。对装饰器这一块之前没有过多的深究,平时工作中也没有用过。翻了一下NestJS
的源码,发现是利用Reflect
实现的,具体的没有细究(我才看了NestJS
半天时间),就去看了一下Reflect
的知识,然后实现一个简单版的,记录一下。
function argA() {
return (target, propertyKey, paramsIndex) => {
Reflect.defineMetadata('argA', paramsIndex, target[propertyKey]);
};
}
function argB() {
return (target, propertyKey, paramsIndex) => {
Reflect.defineMetadata('argB', paramsIndex, target[propertyKey]);
};
}
class T {
a(@argA() a) {
console.log(a);
}
b(@argB() b) {
console.log(b);
}
c(@argA() a, @argB() b) {
console.log(a, b);
}
d(@argB() b, @argA() a) {
console.log(b, a);
}
}
上面实现了argA
和argB
两个装饰器,分别定义了两个元数据(metaData)用来存储参数位置(paramsIndex),然后声明了一个测试类T
,有三个成员方法a
、b
、c
、d
,每个方法的参数个数或顺序均不同。
然后定义一个执行方法run
并执行:
const run = (funcName: string) => {
const argAIndex = Reflect.getMetadata('argA', new T()[funcName]);
const argBIndex = Reflect.getMetadata('argB', new T()[funcName]);
const params: any = [];
if (argAIndex >= 0) {
params[argAIndex] = 'argA';
}
if (argBIndex >= 0) {
params[argBIndex] = 'argB';
}
new T()[funcName](...params);
};
run('a');
run('b');
run('c');
run('d');
控制台输出结果为:
argA
argB
argA argB
argB argA
可以看到结果和预期相符。