装饰器

212 阅读2分钟

装饰器现状

启用

{
    "compilerOptions": {
        "target": "ES5",
        "experimentalDecorators": true
    }
}

简介

装饰器是一种特殊类型的声明,它能够被附加到类声明方法, 访问符属性参数上。 装饰器使用 @expression这种形式,expression求值后必须为一个函数,它会在运行时被调用,被装饰的声明信息做为参数传入。

实现

  • 不支持参数的装饰器
function sealed(target) {
    // do something with "target" ...
}
  • 装饰器工厂
function color(value: string) { // 这是一个装饰器工厂
    return function (target) { //  这是装饰器
        // do something with "target" and "value"...
    }
}

装饰器可以应用在哪些对象上?

知道装饰器的基本概念之后,自然也需要知道到底可以用来装饰什么东西,简单总结就是类相关的它都可以装饰:

  • 类(Class Decorators)
  • 类属性(Property Decorators)
  • 类访问器(Accessor Decorators)
  • 类方法(Method Decorators)
  • 类方法参数(Parameter Decorators)

类装饰器 - Class Decorators

作用:可以拿到类的构造函数,可以修改或替换类的定义

作用对象:类

参数:类的构造函数

返回值:如果带返回值,则需要是一个构造函数,该构造函数将替换原本类的实现

如下示例,返回一个新的类替换原本类的实现:

function decorateClass(constructor) {
  return class B extends constructor {
    name = 'B';
    age = 18;
  }
}

@decorateClass
class A {
  name = 'A';
  constructor() {
  }
}

const a = new A();
console.log(a.name, a.age)  // 输出 B 18
复制代码
function decorateClass(name) {
  return function (constructor) {
    return class B extends constructor {
      name = name || 'B';
      age = 18;
    };
  };
}

@decorateClass('name')
class A {
  name = 'A';
  constructor() {}
}

const a = new A();
console.log(a.name, a.age); // 输出 name 18
复制代码

类属性装饰器 - Property Decorators

作用:可以拿到属性名,对属性实现代理等操作

作用对象:静态成员、实例成员

参数

  1. target:对于静态成员则是构造函数,对于实例成员则是类的原型
  2. key:成员名

返回值:无

如下示例,对装饰的属性实现代理,检查设置的值是否合法:

function validProperty(validNameList: string[]) {
  return function (target: any, propertyKey: string) {
    let value: string = target[propertyKey];

    const get = function () {
      console.log(`${propertyKey} value: ${value}`);
      return value;
    };

    const set = function (val: string) {
      console.log('validNameList', validNameList);
      if (!validNameList.includes(val)) {
        console.warn(`invalid name: ${val}`);
        return;
      }
      value = val;
    };
    Object.defineProperty(target, propertyKey, { set, get });
  };
}

class People {
  @validProperty(['a', 'b'])
  name: string;

  constructor(name: string) {
    this.name = name;
  }
}

const people = new People('a');

console.log(people.name); // a
people.name = 'cccc'; // invalid name: cccc
console.log(people.name); // a
复制代码

类访问器装饰器 - Accessor Decorators

作用:可以修改或替换访问器的定义

作用对象:静态访问器、实例访问器

参数

  1. target:对于静态访问器则是构造函数,对于实例访问器则是类的原型
  2. key:访问器名
  3. descriptor:属性描述符,即 Object.getOwnPropertyDescriptor(target,key)

返回值:如果带返回值,则需要是一个属性描述符,并作为访问器的属性描述符

如下示例,修改访问器的属性描述符:

function enumerable(value: boolean) {
  return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
    descriptor.enumerable = value;
  };
}

class People {
  @validProperty(['a', 'b'])
  firstName: string;

  lastName: string;

  @enumerable(false)
  get fullName() {
      return `${this.firstName} ${this.lastName}`;
  }

  constructor(firstName: string, lastName: string) {
    this.firstName = firstName;
    this.lastName = lastName;
  }
}
复制代码

类方法装饰器 - Method Decorators

作用:可以修改或替换方法的定义

作用对象:静态方法、实例方法

参数

  1. target:对于静态方法则是构造函数,对于实例方法则是类的原型
  2. key:方法名
  3. descriptor:属性描述符,即 Object.getOwnPropertyDescriptor(target,key)

返回值:如果带返回值,则需要是一个属性描述符,并作为访问器的属性描述符

如下示例,增强方法,补充方法弃用提示:

const deprecated = (deprecationReason: string) => {
  return (target: any, memberName: string, propertyDescriptor: PropertyDescriptor) => {
    const wrapperFn = (...args: any[]) => {
        console.warn(`Method ${memberName} is deprecated with reason: ${deprecationReason}`);
        propertyDescriptor.value.apply(target, args)
    }
    return {
      value: wrapperFn,
    }
  }
}

class TestClass {
  static staticMember = true;

  instanceMember: string = "hello"

  @deprecated("Use another static method")
  static deprecatedMethodStatic() {
    console.log('inside deprecated static method - staticMember =', this.staticMember);
  }

  @deprecated("Use another instance method")
  deprecatedMethod () {
    console.log('inside deprecated instance method - instanceMember =', this.instanceMember);
  }
}

TestClass.deprecatedMethodStatic();

const instance = new TestClass();
instance.deprecatedMethod();
复制代码

类方法参数装饰器 - Parameter Decorators

作用:待补充

作用对象:静态方法、实例方法

参数

  1. target:对于静态成员则是构造函数,对于实例成员则是类的原型
  2. key:参数名
  3. index:参数在函数参数列表中的索引

返回值:无

如下示例:

function print(target: Object, propertyKey: string, parameterIndex: number) {
  console.log(`Decorating param ${parameterIndex} from ${propertyKey}`);
}

class TestClass {
  testMethod(param0: any, @print param1: any) {}
}
复制代码

总结

  • 装饰器本质上就是一个包装函数。它可以拿到被装饰的内容,对其进行自定义加工和处理。