Skip to content
目录

基础篇-装饰器

什么是装饰器

装饰器是一种特殊类型的声明,它可以被附加到类声明,方法,属性或参数上,可以修改类的行为。通俗的讲,装饰器就是一个方法,可以注入到类、方法、属性、参数上来扩展类、方法、属性、参数的功能。

装饰器调用顺序:属性装饰器>方法参数装饰器>方法装饰器>静态属性装饰器>静态方法装饰器>类装饰器

定义:相同装饰器按右边>左边 ,下面>上面
执行:相同装饰器按左边>右边 ,上面>下面

以下两种风格均是合法的

ts
@f @g x
ts
@f
@g
x

在 TypeScript 中装饰器还属于实验性语法,所以要想使用必须在配置文件中 tsconfig.json 编译选项中开启

ts
{
    "compilerOptions": {
        "experimentalDecorators": true
    }
}

普通装饰器

ts
/**
 * 装饰器
 * target:代表当前修饰的类
 */
function logClass(target: any) {
  console.log(target);
  target.prototype.data = 'dataChange';
}

@logClass
class Operator {
  data: string | undefined;
  constructor() {}

  getData(): any {
    console.log('data:', this.data);
  }
}

const newOperator = new Operator();
newOperator.getData(); // dataChange
console.log(newOperator.data); // dataChange

装饰器工厂

ts
/**
 * params:类装饰器传入的参数
 * target:当前修饰的类
 */
function logClass1(params: string) {
  console.log('装饰器1');
  return function (target: any) {
    console.log('params1:', params);
    console.log(target);
    target.prototype.data = params;
  };
}
function logClass2(params: string) {
  console.log('装饰器2');
  return function (target: any) {
    console.log('params2:', params);
    console.log(target);
    target.prototype.name = params;
  };
}

@logClass1('hello')
@logClass2('world')
class TestClass {
  name: string | undefined;
  data: string | undefined;
  constructor() {}
  getData(): any {
    console.log('name:', this.name);
    console.log('data:', this.data);
  }
}

控制台输出顺序

ts
/* 装饰器1
装饰器2
params2: world
TestClass() {}
params1: hello
TestClass() { } */

不同类型的装饰器

装饰器执行顺序:属性装饰器>方法装饰器>参数装饰器>类装饰器 如果有多个同类型的装饰器,则执行顺序是从后到前执行。

ts
// 装饰器调用顺序
// 属性装饰器>方法参数装饰器>方法装饰器>静态属性装饰器>静态方法装饰器>类装饰器
// 定义:相同装饰器按右边>左边 ,下面>上面
// 执行:相同装饰器按左边>右边 ,上面>下面

/**
 * 属性装饰器
 * @param targetPrototype 构造函数的原型
 * @param propName 属性名称
 */
function propDecorator1(targetPrototype: any, propName: string) {
  console.log('属性装饰器1:', targetPrototype, propName);
}

function propDecorator2(params: string) {
  console.log('属性装饰器2 before:', params);
  return function (targetPrototype: any, propName: string) {
    // targetPrototype[propName]是在原型链上查找实例属性,永远为undefined
    console.log(
      '属性装饰器2:',
      targetPrototype,
      propName,
      targetPrototype[propName],
    );
    targetPrototype[propName] = '会在原型上面添加该属性并赋值,不是实例对象';
  };
}

function propDecorator3(params: string) {
  console.log('属性装饰器3 before:', params);
  return function (targetPrototype: any, propName: string) {
    console.log('属性装饰器3:', targetPrototype, propName);
  };
}

/**
 * 方法装饰器
 * @param targetPrototype 构造函数的原型
 * @param methodName 方法名称
 * @param descr 方法的描述
 */
function methodDecorator1(
  targetPrototype: any,
  methodName: string,
  descriptor: PropertyDescriptor,
) {
  console.log('方法装饰器1:', targetPrototype, methodName, descriptor);
}

let methodDecorator2: any = function (params: string) {
  console.log('方法装饰器2 before:', params);
  return function (
    targetPrototype: any,
    methodName: string,
    descriptor: PropertyDescriptor,
  ) {
    console.log('方法装饰器2:', targetPrototype, methodName, descriptor);
  };
};

let methodDecorator3: any = function (params: string) {
  console.log('方法装饰器3 before:', params);
  return function (
    targetPrototype: any,
    methodName: string,
    descriptor: PropertyDescriptor,
  ) {
    console.log('方法装饰器3:', targetPrototype, methodName, descriptor);
    let originalMethod = descriptor.value;
    descriptor.value = function (...args: any[]) {
      args = args.map((item) => (item += ' test'));
      return originalMethod.apply(this, args);
    };
    return descriptor;
  };
};

/**
 * 方法参数装饰器
 * @param targetPrototype 构造函数的原型
 * @param methodName 方法名称
 * @param paramIndex 参数在arguments中的下标
 */
function paramDecorator1(
  targetPrototype: any,
  methodName: string,
  paramIndex: number,
) {
  console.log('方法参数装饰器1:', targetPrototype, methodName, paramIndex);
}

function paramDecorator2(params: string) {
  console.log('方法参数装饰器2 before:', params);
  return function (
    targetPrototype: any,
    methodName: string,
    paramIndex: number,
  ) {
    console.log('方法参数装饰器2:', targetPrototype, methodName, paramIndex);
  };
}

function paramDecorator3(params: string) {
  console.log('方法参数装饰器3 before:', params);
  return function (
    targetPrototype: any,
    methodName: string,
    paramIndex: number,
  ) {
    console.log('方法参数装饰器3:', targetPrototype, methodName, paramIndex);
  };
}

/**
 * 静态属性修饰器
 * @param targetPrototype
 * @param propName
 */
function staticPropDecorator(param: string) {
  console.log('静态属性修饰器 before:', param);
  return function (targetPrototype: any, propName: string) {
    console.log(
      '静态属性修饰器:',
      targetPrototype,
      propName,
      targetPrototype[propName],
    );
    targetPrototype[propName] = '静态属性初始值被修改了!';
  };
}

/**
 * 静态方法修饰器
 * @param targetPrototype
 * @param methodName
 * @param descriptor
 */
function staticMethodDecorator(param: string) {
  console.log('静态方法修饰器 before:', param);
  return function (
    targetPrototype: any,
    methodName: string,
    descriptor: PropertyDescriptor,
  ) {
    console.log('静态方法修饰器:', targetPrototype, methodName, descriptor);
  };
}

/**
 * 类装饰器
 * @param targetClass
 */
function classDecorator1(constructor: Function) {
  console.log('类装饰器1:', constructor);
}

function classDecorator2(params: string) {
  console.log('类装饰器2 before:', params);
  return function (constructor: Function) {
    console.log('类装饰器2:', constructor);
  };
}

function classDecorator3(params: string) {
  console.log('类装饰器3 before:', params);
  return function (constructor: Function) {
    console.log('类装饰器3:', constructor);
  };
}

@classDecorator1
@classDecorator2('params2')
@classDecorator3('params3')
class Test {
  @propDecorator1
  @propDecorator2('param2')
  @propDecorator3('param3')
  public msg: string = '属性初始值';

  @staticPropDecorator('静态属性')
  static title: string = '静态属性初始值';

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

  @methodDecorator1
  @methodDecorator2('param2')
  @methodDecorator3('param3')
  toString(
    @paramDecorator1 str1: string,
    @paramDecorator2('param2') str2: string,
    @paramDecorator3('param3') str3: string,
  ) {
    console.log('toString:', str1, str2, str3);
  }

  @staticMethodDecorator('静态方法')
  static staticToString() {
    console.log(this.title);
  }
}

let t: any = new Test('this is a msg.');
t.toString('ss', 'dd', 'ff'); //methodDecorator3装饰器对该方法进行了重写
console.log(t.msg, t.__proto__.msg);
Test.staticToString();

运行结果:

ts
/* 属性装饰器2 before: param2
属性装饰器3 before: param3
属性装饰器3: Test { toString: [Function] } msg
属性装饰器2: Test { toString: [Function] } msg undefined
属性装饰器1: Test { toString: [Function], msg: '会在原型上面添加该属性并赋值,不是实例对象' } msg
方法装饰器2 before: param2
方法装饰器3 before: param3
方法参数装饰器2 before: param2
方法参数装饰器3 before: param3
方法参数装饰器3: Test { toString: [Function], msg: '会在原型上面添加该属性并赋值,不是实例对象' } toString 2
方法参数装饰器2: Test { toString: [Function], msg: '会在原型上面添加该属性并赋值,不是实例对象' } toString 1
方法参数装饰器1: Test { toString: [Function], msg: '会在原型上面添加该属性并赋值,不是实例对象' } toString 0
方法装饰器3: Test { toString: [Function], msg: '会在原型上面添加该属性并赋值,不是实例对象' } toString { value: [Function],
  writable: true,
  enumerable: true,
  configurable: true }
方法装饰器2: Test { toString: [Function], msg: '会在原型上面添加该属性并赋值,不是实例对象' } toString { value: [Function],
  writable: true,
  enumerable: true,
  configurable: true }
方法装饰器1: Test { toString: [Function], msg: '会在原型上面添加该属性并赋值,不是实例对象' } toString { value: [Function],
  writable: true,
  enumerable: true,
  configurable: true }
静态属性修饰器 before: 静态属性
静态属性修饰器: function Test(msg) {
        this.msg = "属性初始值";
        this.msg = msg;
    } title 静态属性初始值
静态方法修饰器 before: 静态方法
静态方法修饰器: function Test(msg) {
        this.msg = "属性初始值";
        this.msg = msg;
    } staticToString { value: [Function],
  writable: true,
  enumerable: true,
  configurable: true }
类装饰器2 before: params2
类装饰器3 before: params3
类装饰器3: function Test(msg) {
        this.msg = "属性初始值";
        this.msg = msg;
    }
类装饰器2: function Test(msg) {
        this.msg = "属性初始值";
        this.msg = msg;
    }
类装饰器1: function Test(msg) {
        this.msg = "属性初始值";
        this.msg = msg;
    }
toString: ss test dd test ff test
this is a msg. 会在原型上面添加该属性并赋值,不是实例对象
静态属性初始值被修改了! */