装饰器

175 阅读3分钟
  1. 启用装饰器

    • 在tsconfig.json 中开启 experimentalDecorators: true
    • 装饰器是一种特殊类型的声明,它能够被附加到类声明,方法,访问符,属性,或者参数上
    • 使用方式 @装饰器名称 , 后面必须跟一个函数,会在运行时被调用
  2. 装饰器工厂

    // 装饰器工厂函数
    function f() {
        console.log("f(): evaluated");
        return function (target, propertyKey: string, descriptor: PropertyDescriptor) {
            console.log("f(): called");
        }
    }
    // 装饰器工厂函数
    function g() {
        console.log("g(): evaluated");
        return function (target, propertyKey: string, descriptor: PropertyDescriptor) {
            console.log("g(): called");
        }
    }
    
    class C {
        @f() // 装饰器
        @g() // 装饰器
        method() {}
    }
    
  3. 装饰器求职

    • 类中不同声明上的装饰器将按以下规定的顺序应用:
      • 参数装饰器,然后依次是方法装饰器访问符装饰器,或属性装饰器应用到每个实例成员。
      • 参数装饰器,然后依次是方法装饰器访问符装饰器,或属性装饰器应用到每个静态成员。
      • 参数装饰器应用到构造函数。
      • 类装饰器应用到类。
  4. 类装饰器

    // 类装饰器表达式会在运行时当作函数被调用,类的构造函数作为其唯一的参数。
    // 如果类装饰器返回一个值,它会使用提供的构造函数来替换类的声明。
    
    function classDecorator<T extends {new(...args:any[]):{}}>(constructor:T) {
        return class extends constructor {
            newProperty = "new property";
            hello = "override";
        }
    }
    
    @classDecorator
    class Greeter {
        property = "property";
        hello: string;
        constructor(m: string) {
            this.hello = m;
        }
    }
    
    console.log(new Greeter("world"));
    
  5. 方法装饰器

    • 方法装饰器表达式会在运行时当作函数被调用,传入下列3个参数:
      • 对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。
      • 成员的名字。
      • 成员的属性描述符
    • 如果方法装饰器返回一个值,它会被用作方法的属性描述符
    // 先定义一个方法
    function enumerable(value: boolean) {
        return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
            descriptor.enumerable = value;
        };
    }
    
    class Greeter {
        greeting: string;
        constructor(message: string) {
            this.greeting = message;
        }
    
        @enumerable(false) // 在类中将上面的方法设置为装饰器
        greet() {
            return "Hello, " + this.greeting;
        }
    }
    
  6. 访问器装饰器

    • 访问器装饰器表达式会在运行时当作函数被调用,传入下列3个参数:
      • 对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。
      • 成员的名字。
      • 成员的属性描述符
    function configurable(value: boolean) {
        return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
            descriptor.configurable = value;
        };
    }
    class Point {
        private _x: number;
        private _y: number;
        constructor(x: number, y: number) {
            this._x = x;
            this._y = y;
        }
    
        @configurable(false)
        get x() { return this._x; }
    
        @configurable(false)
        get y() { return this._y; }
    }
    
    
  7. 属性装饰器

    • 属性装饰器表达式会在运行时当作函数被调用,传入下列2个参数:
      • 对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。
      • 成员的名字。
    import "reflect-metadata";
    
    const formatMetadataKey = Symbol("format");
    
    function format(formatString: string) {
        return Reflect.metadata(formatMetadataKey, formatString);
    }
    
    function getFormat(target: any, propertyKey: string) {
        return Reflect.getMetadata(formatMetadataKey, target, propertyKey);
    }
    
    class Greeter {
        @format("Hello, %s")
        greeting: string;
    
        constructor(message: string) {
            this.greeting = message;
        }
        greet() {
            let formatString = getFormat(this, "greeting");
            return formatString.replace("%s", this.greeting);
        }
    }
    
  8. 参数装饰器

    • 参数装饰器表达式会在运行时当作函数被调用,传入下列3个参数:
      • 对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。
      • 成员的名字。
      • 参数在函数参数列表中的索引。
    class Greeter {
        greeting: string;
    
        constructor(message: string) {
            this.greeting = message;
        }
    
        @validate
        greet(@required name: string) {
            return "Hello " + name + ", " + this.greeting;
        }
    }