Decorator

104 阅读3分钟

一、关键点

  • 1、发生时间

装饰器对类的行为的改变,是代码编译时发生的,而不是在运行时。这意味着,装饰器能在编译阶段运行代码。也就是说,装饰器本质就是编译时执行的函数。

  • 2、要求

只能在类里使用

  • 3、本质

是一个函数

二、分类

类装饰器

目标

  • 应用于类的构造函数

参数

  • 第一个参数(也只有一个参数)target
    类的构造函数作为其唯一的参数

属性装饰器

访问器装饰器

目标

  • 应用于类的访问器(getter、setter)上

参数

  • 第一个参数
    • 静态方法:类的构造函数
    • 实例方法:类的原型对象
  • 第二个参数
    • 属性名称
  • 第三个参数
    • 方法描述符对象

方法装饰器

目标

  • 应用于类的方法上

参数

  • 第一个参数target
    • 静态方法static:类的构造函数
    • 实例方法:类的原型对象
  • 第二个参数name
    • 被装饰的方法名称
  • 第三个参数descriptor:PropertyDescriptor
    • 方法描述符对象

参数装饰器

目标

  • 应用在参数上

参数

  • 第一个参数
    • 静态方法:类的构造函数
    • 实例方法:类的原型对象
  • 第二个参数
    • 方法名称
  • 第三个参数
    • 参数在函数参数列表中的索引

三、执行顺序

function d1(target: Function) {
    console.log('----------->类装饰器-------------》d1');
    console.log(target);
    console.log(typeof target);
}

function d2(target: any, name: string) {
    console.log('----------->属性装饰器-------------》d2');
    console.log(typeof target, name);
}

function d3(target: any, name: string, descriptor: PropertyDescriptor) {
    console.log('----------->访问器装饰器-------------》d3');
    console.log(typeof target, name);
    console.log(descriptor);
}

function d4(target: any, name: string, descriptor: PropertyDescriptor) {
    console.log('----------->方法装饰器-------------》d4');
    console.log(typeof target, name);
    console.log(descriptor);
}

function d5(target: any, name: string, index: number) {
    console.log('----------->参数装饰器-------------》d5');
    console.log(typeof target, name);
    console.log(index);
}
@d1
class HelloClass {
    @d2
    public age: number
    @d2
    static property1: number

    @d3
    get name() {
        return 'lxq';
    }
    @d3
    static get like() {
        return '游泳'
    }
    @d4
    public method1(@d5 x: number) {

    }
    @d4
    public static method2(x: string, @d5 y: number) {

    }
}

执行结果:

/**
 * 执行顺序
 * 类似洋葱圈
 */
// step1 : 实例装饰器
// 属性=>访问器=>参数=>方法

// step2 : 静态装饰器
// 属性=>访问器=>参数=>方法

// step3 : 类装饰

四、复合

/**
 * 复合装饰器
 * 执行顺序:类似洋葱圈
 */

function d11(target: Function) {
    console.log('----------->类装饰器-------------》d11');
    console.log(target);
    console.log(typeof target);
}
@d1 @d11
class WorldClass { }

五、工厂

// 类的装饰器:类的继承
function InstanceClass<T extends { new(...agrs: any[]): {} }>(ThisConstructor: T) {
    return class Contructor extends ThisConstructor {
        constructor(...args: any[]) {
            super(args);
        }
        sayHello() {
            console.log('Hello everyone');
        }
        sayAge() {
            console.log('my age is 31');
        }
    }
}
// 类的装饰器:实例属性扩展
function addProperty(propertyName: string) {
    return (target: Function) => {
        target.prototype.name = propertyName;
    }
}
// 类的装饰器:静态属性扩展
function addStaticProperty(propertyName: string) {
    return (target: any) => {
        target.gender = propertyName;
    }
}

@addStaticProperty('Man')
@addProperty('Cjet')
@InstanceClass
class Test {
    constructor() { }
    static gender:string;
    name: string;

    errorMsg: Error = new Error('方法未实现');
    sayHello() {
        throw this.errorMsg;
    }
    sayAge() {
        throw this.errorMsg;
    }
    tellMe() {
        this.sayHello();
        this.sayAge();
    }
}
const test = new Test();

console.log(test.name);
console.log(Test.gender);
test.tellMe();

六、工厂-方法装饰

// 方法装饰器
function log(target: Function, propertyName: string, descriptor: PropertyDescriptor) {
    console.log('编译时:执行', target);
    let oldValue = descriptor.value;
    descriptor.value = function () {
        console.log(`target🌶  : ${target}`);
        console.log(`propertyName: ${propertyName} with`, arguments);
        return oldValue.apply(this, arguments);
    }
    // return descriptor; // 可以省略
}

function readMoney(houseRent: string) {
    return (target: Object, name: string, descriptor: PropertyDescriptor) => {
        console.log('编译时:执行', target);
        // console.log(name);
        // console.log(descriptor);
        let oldValue = descriptor.value;
        descriptor.value = (...args: any[]) => {
            return houseRent + "" + oldValue.apply(this, [...args, houseRent]);
        }
        // return descriptor; // 可以省略
    }
}

class M {
    @log
    static add(a: number, b: number) {
        return a + b;
    }
    @readMoney("$")
    salary(baseSalary: number, Advanced: number,): number {
        return baseSalary + Advanced;
    }
    @readMoney("¥")
    earn(basePrice: number): number {
        return basePrice;
    }

}
const result = M.add(12, 13);
console.log(result);

const m = new M();
const salaryRes = m.salary(2000, 1000);
console.log(salaryRes);
const sarnRes = m.earn(15000);
console.log(sarnRes);

参考链接

ts:装饰器