在了解nest js之前,要先对装饰器有进一步的认识。装饰器并不是ts所提供的特性,而是ts实现的ECMAScript提案。
为什么需要装饰器?基于装饰器我们能够快速优雅的复用逻辑,提供注释一样的解释说明效果,以及对业务代码能力的增强。同时,依赖注入也可以通过装饰器来简洁的实现。
1. 不同的装饰器
类装饰器
function addProp(construtor: Function){
function.prototype.job = 'fe';
}
// 这里的函数就是一个装饰器,参数为被装饰的类的构造函数
@addPropp
class P{
job: string;
constructor(public name: string){};
}
let p = new p('ponny');
console.log(p.job); // 'fe'
function addProp(param: string): ClassDerector {
return (constructor: Function) => {
constructor.prototype.job = param;
}
}
// 这里的函数接受一个参数,并返回一个装饰器
@addProp('fefb')
class P{
job: string;
constructor(public name: string){};
}
let p = new p('ponny');
console.log(p.job); // 'fefb'
方法装饰器
function addProps(): MesthidDesrector{
return (taregt, properrtykey, descriptor) => {
cnsole.log(target); // 类原型对象
console.log(propertyKey); //修饰的方法
console.log(descriptor); // 改方法的属性描述符
descriptor.writable = false;
}
}
class A{
@addProp()
originMethod() {
console.log('Originnal');
}
}
const a = new A();
a.originMethod = () => { cosnole.log(changed!); }
a.originMethod(); // 'OrigiMethod' ,这是装饰器的作用
函数addProp()返回一个方法装饰器,第三个参数descriptor有: writeable, enumerable, configurable
属性装饰器
function addProp(): PropertyDecoretor {
retun (trget, propertyKey) => {
console.log(propertyKey);
};
}
class A {
@addProp()
originProps: any;
}
参数装饰器
function paramDeco(params?:any): ParameterDecorator {
return (target, propertyKey, index) {
conole.log(target);
console.log(propertyKey);
console.log(index);
target.constructor.prototype.fromParamDeco = test';
};
}
class B {
someMethod(@parmaDeco param1: any, @paramDeco param2: any){
console.log(param1,param2);
}
}
new B().someMethod();
console.log(B.prototype.fromParamDecoretor);
2. 装饰器工厂
如果我们同时需要上面四种装饰器,可以考虑接入装饰器工厂,使用一个装饰器工厂来为我们根据条件吐出不同的装饰器来。
首先准备好各个装饰器函数,最好不要把功能写在里面,避免造成耦合
function classDeco(): ClassDecorator {
return (target: Object) => {
console.log('Class Decorator Invoked');
console.log(target);
};
}
function propDeco(): PropertyDecorator {
return (target: Object, propertyKey: string) => {
console.log('Property Decorator Invoked');
console.log(propertyKey);
};
}
function methodDeco(): MethodDecorator {
return (
target: Object,
propertyKey: string,
descriptor: PropertyDescriptor
) => {
console.log('Method Decorator Invoked');
console.log(propertyKey);
};
}
function paramDeco(): ParameterDecorator {
return (target: Object, propertyKey: string, index: number) => {
console.log('Param Decorator Invoked');
console.log(propertyKey);
console.log(index);
};
}
接着实现一个工厂函数,使得根据不同的条件/情况返回不同的装饰器
enum DecorateType {
CLASS = 'class';
METHOD = 'method';
PROPERTY = 'property';
PARAM = 'param';
}
type FactoryReturnType =
| ClassDecorater
| MethodDecorator
| Propertydecorator
| ParamDecorator;
function decoFactory(type: DecoratorTytpe, ...argss:any[]): FactotyReturetype {
switch(type) {
case DecorateType.CLASS:
return classDeco.apply(this, args);
case DecoratorType.METHOD:
return methodDeco.apply(this, args);
case DecoratorType.PROPERTY:
return propertyDeco.apply(this, args);
case DecoratorType.PARAM:
return propertyDeco.apply(this, args);
default:
throw new Error('invlaid decorator type');
}
}
@decoFactory(DecoratorType.CLASS)
class C {
@decoFactory(DecoratorType.PROPERTY)
prop: any;
@decoFactory(DecoratorType.METHOD)
method(@decoFactory(DecoratorType.PARAM) param: string) {}
}
3. 多个装饰器的应用顺序:
4. 多个装饰器来装饰同一个声明
function foo() {
console.log("foo in");
return function (target, propertyKey: string, descriptor: PropertyDescriptor) {
console.log("foo out");
}
}
function bar() {
console.log("bar in");
return function (target, propertyKey: string, descriptor: PropertyDescriptor) {
console.log("bar out");
}
}
class A {
@foo()
@bar()
method() {}
}
// foo in
// bar in
// bar out
// foo out