Skip to content
大纲

Decorator 装饰器

装饰器是 TypeScript 中的一种特殊类型的声明,它可以被附加到类声明、方法、属性或参数上,以修改类的行为。装饰器使用 @expression 这种形式,其中 expression 求值后必须为一个函数,它会在运行时被调用,被装饰的声明信息作为参数传入。

在 TypeScript 中,装饰器有四种类型:类装饰器属性装饰器方法装饰器方法参数装饰器。类装饰器用于类声明,属性装饰器用于属性声明,方法装饰器用于方法声明,方法参数装饰器用于方法参数声明。

装饰器可以用来扩展类的行为,比如添加属性和方法,或者修改类的行为。装饰器可以被多次应用,它们的执行顺序是从下往上,从右往左。如果有多个同样的装饰器,它会先执行后面的。

这篇文章介绍了 TypeScript 中的装饰器,包括类装饰器、属性装饰器、方法装饰器和方法参数装饰器。它还展示了如何使用装饰器来修改类的行为,或者添加一些属性和方法。最后,它还介绍了装饰器的执行顺序,以及如何在多个装饰器之间进行排序。

1. 类装饰器

1.1 类装饰器: 普通装饰器(无法传参)

ts
// 定义一个类装饰器,用于在类上添加一个 apiUrl 属性和一个 run 方法
const LogClass: ClassDecorator = (target: any) => {
    console.log(target);
    // target 就是当前类
    target.prototype.apiUrl = '动态扩展的属性';
    target.prototype.run = function () {
        console.log('我是一个run方法');
    };
};

// 使用 LogClass 装饰器装饰 HttpClient 类
@LogClass
class HttpClient {
    constructor() {}
    getData() {}
}

// 创建一个 HttpClient 实例,并输出它的 apiUrl 属性和调用它的 run 方法
let http: any = new HttpClient();
console.log(http.apiUrl); // 动态扩展的属性
http.run(); // 我是一个run方法

1.2 类装饰器: 装饰器工厂(可传参)

ts
// 定义一个类装饰器工厂,用于在类上添加一个 apiUrl 属性
const LogClass2: (params: string) => ClassDecorator = params => {
    return (target: any) => {
        console.log(target);
        console.log(params);
        target.prototype.apiUrl = params;
    };
};

// 使用 LogClass2 装饰器装饰 HttpClient2 类
@LogClass2('http://www.itying.com/api')
class HttpClient2 {
    constructor() {}
    getData() {}
}

// 创建一个 HttpClient2 实例,并输出它的 apiUrl 属性
let http2: any = new HttpClient2();
console.log(http2.apiUrl);

2. 属性装饰器

ts
// 定义一个属性装饰器,用于在属性上添加一个 url 属性
const LogAttribute: (param: string) => PropertyDecorator = param => {
    return (target: any, attrName: any) => {
        console.log(target);
        console.log(attrName);
        target[attrName] = param;
    };
};

// 定义一个 HttpClient3 类,并使用 LogAttribute 装饰器装饰它的 url 属性
class HttpClient3 {
    @LogAttribute('https://www.google.com')
    public url: any | undefined;
    constructor() {}
    getData() {}
}

// 创建一个 HttpClient3 实例,并输出它的 url 属性
let http3: any = new HttpClient3();
console.log(http3.url);

3. 方法装饰器

ts
// 定义一个方法装饰器,用于在方法上添加一个 apiUrl 属性和一个 run 方法
const LogMethod: (param: any) => MethodDecorator = param => {
    return (target: any, methodName: any, desc: any) => {
        console.log(target);
        console.log(methodName);
        console.log(desc);
        target.apiUrl = param;
        desc.value = function () {
            console.log(param);
        };
    };
};

// 定义一个 HttpClient4 类,并使用 LogMethod 装饰器装饰它的 getData 方法
class HttpClient4 {
    public url: string | undefined;
    constructor() {}
    @LogMethod('https://www.google.com')
    getData() {}
}

// 创建一个 HttpClient4 实例,并调用它的 getData 方法
let http4: any = new HttpClient4();
http4.getData();

4. 方法参数装饰器

ts
// 定义一个方法参数装饰器,用于在方法参数上输出参数信息
const LogParams: ParameterDecorator = (target: any, methodName: any, paramsIndex: any) => {
    console.log(target);
    console.log(methodName);
    console.log(paramsIndex);
};

// 定义一个 HttpClient5 类,并使用 LogParams 装饰器装饰它的 getData 方法
class HttpClient5 {
    public url: string | undefined;
    constructor() {}
    getData(@LogParams uuid: any) {
        console.log(uuid);
    }
}

// 创建一个 HttpClient5 实例,并调用它的 getData 方法
let http5: any = new HttpClient5();
http5.getData(123456);

5. 多个装饰器的执行顺序

ts
// 定义一个类装饰器工厂,用于在类上添加一个 apiUrl 属性
const LogClass2: (params: string) => ClassDecorator = params => {
    return (target: any) => {
        console.log('LogClass2');
        console.log(params);
        console.log(target);
    };
};

// 定义一个属性装饰器,用于在属性上添加一个 url 属性
const LogAttribute2: (params: string) => PropertyDecorator = params => {
    return (target: any, attrName: any) => {
        console.log('LogAttribute2');
        console.log(params);
        console.log(target);
        console.log(attrName);
    };
};

// 定义一个方法装饰器,用于在方法上添加一个 apiUrl 属性
const LogMethod2: (params: string) => MethodDecorator = params => {
    return (target: any, methodName: any, desc: any) => {
        console.log('LogMethod2');
        console.log(params);
        console.log(target);
        console.log(methodName);
        console.log(desc);
    };
};

// 定义一个方法参数装饰器,用于在方法参数上输出参数信息
const LogParams2: (params: string) => ParameterDecorator = params => {
    return (target: any, methodName: any, paramsIndex: any) => {
        console.log('LogParams2');
        console.log(params);
        console.log(target);
        console.log(methodName);
        console.log(paramsIndex);
    };
};

// 定义一个 HttpClient6 类,并使用多个装饰器装饰它的属性、方法和方法参数
@LogClass2('http://www.itying.com/api')
class HttpClient6 {
    constructor() {}
    @LogAttribute2('xxxx')
    public url: any | undefined;
    @LogMethod2('get')
    getData() {}
    getData2(@LogParams2('uuid') uuid: any) {
        console.log(uuid);
    }
}

// 创建一个 HttpClient6 实例,并输出它的 apiUrl 属性
let http6: any = new HttpClient6();
console.log(http6.apiUrl);

// 定义一个 HttpClient8 类,并使用 LogParams2 装饰器装饰它的 getData 方法
class HttpClient8 {
    public url: string | undefined;
    constructor() {}
    getData(@LogParams2('uuid') uuid: any) {
        console.log(uuid);
    }
}

// 创建一个 HttpClient8 实例,并调用它的 getData 方法
let http8: any = new HttpClient8();
http8.getData(123456); // 123456
console.log(http8.apiUrl); // uuid

// 装饰器执行顺序
// 属性 > 方法 > 方法参数 > 类
// 如果有多个同样的装饰器, 它会先执行后面的
// 1. 属性装饰器
// 2. 方法装饰器
// 3. 方法参数装饰器
// 4. 类装饰器