TS装饰器

什么是装饰器

装饰器是一种特殊类型的声明,他可以用在类声明、方法、属性或者参数上。顾名思义,它是用来给附着的主体进行装饰,添加额外的行为。装饰器使用 @expression 这种形式,expression求值后必须为一个函数,它会在运行时被调用,被装饰的声明信息作为参数传入

装饰器的分类与执行顺序

@log2 // 类装饰器
@log1 // 类装饰器
class Test {
  private _age: number = 0;
  @nameDecorator // 属性装饰器
  public name: string = "";
  @sayDecorator // 方法装饰器
  public say(
    @paramWhoDecorator who: string, // 参数装饰器
    @paramAnythingDecorator anything: string // 参数装饰器
  ) {
    console.log(`${who}${anything}`);
  }
  @ageDecorator //访问器装饰器
  get age() {
    return this._age + 1;
  }
  set age(v: number) {
    this._age = v;
  }
  @staticDecorator //方法装饰器
  static getSomething() {
    return "getSomething";
  }
}

每种类型的装饰器调用顺序不同的,具体如下:
1.参数装饰器,然后依次是方法装饰器,访问器装饰器,或属性装饰器应用到每个实例成员(即类原型的成员)
2.参数装饰器,然后依次是方法装饰器,访问器装饰器,或属性装饰器应用到每一个静态成员
3.参数装饰器应用到构造函数(即类原型)
4.类装饰器应用到类
例如上述的 say 方法的装饰器:
@paramAnythingDecorator -> @paramWhoDecorator -> @sayDecorator -> @nameDecorator -> @log1

类装饰器

我们可以看一下类装饰器在 typescript 中的定义

declare type ClassDecorator = <TFunction extends Function>(
  target: TFunction
) => TFunction | void;

从ts的底层定义上,类装饰器只有一个参数,而且这个参数是装饰类的构造函数

function log(constructor: any) {
  // 重写类中的方法
  constructor.prototype.getName = () => {
    console.log("getNameOverride");
  }; // 类新增方法
  constructor.prototype.getAge = () => {
    console.log("getAge");
  }; // 类新增属性
  constructor.prototype.age = "age";
  constructor.prototype.name = "xx";
}
@log
class Test {
  name: string;
  constructor(name: string) {
    this.name = name;
  }
  getName() {
    console.log("getName");
  }
}
const test = new Test("name");
test.getName(); // 可以打印出 getNameOverride
test.getAge();
console.log(test.age); // 可以打印出 age
console.log(test.name); 
// 打印的是name,而不是xx,通过装饰器是装饰到类的原型上,所以此处test.__proto__.name打印的才是xx

简单实现一个混合的 mixin

class A {
  getName() {
    return "name";
  }
}
class B {
  getAge() {
    return 18;
  }
}
@mixin(A, B)
class C {}
function mixin(...args: any[]) {
  // 调用函数返回装饰器实际应用的函数
  return function (constructor: any) {
    for (let arg of args) {
      for (let key of Object.getOwnPropertyNames(arg.prototype)) {
        if (key === "constructor") continue; // 跳过构造函数
        Object.defineProperty(
          constructor.prototype,
          key, // @ts-ignore
          Object.getOwnPropertyDescriptor(arg.prototype, key)
        );
      }
    }
  };
}
let c = new C(); // @ts-ignore
console.log(c.getName(), c.getAge()); // 1, 2

方法装饰器

我们可以看一下类装饰器在 ts 中的定义:

declare type MethodDecorator = <T>(
  target: Object,
  propertyKey: string | symbol,
  descriptor: TypedPropertyDescriptor<T>
) => TypedPropertyDescriptor<T> | void;

方法装饰器的以下三个参数:
1.target:如果是普通方法,那么此时的 target 为类的原型。如果为静态方法,target 对应的是类的构造函数
2.propertyKey:为方法的名字
3.descript:PropertyDescriptor 类型

function getNameDescorator(
  target: any,
  key: string,
  descriptor: PropertyDescriptor
) {
  // 1、通过descriptor修改当前方法的属性,不能被重写
  descriptor.writable = false;
}
class Test {
  name: string;
  constructor(name: string) {
    this.name = name;
  }
  @getNameDescorator
  getName() {
    return this.name;
  }
}
const test = new Test("name");
(test as any).getName = () => {
  // 修改此方法就会报错 return 'xx'
};

简单实现一个异步捕获的逻辑,这样可以在多处使用

访问器装饰器

访问器主要用于 get 以及 set 前缀的函数,用于控制属性的肤质及取值操作,在使用上与函数装饰器没什么区别,甚至在返回值的处理上也没有什么区别。并且 ts不允许同时装饰一个成员的 get 和 set 访问器

function prefix(target: any, key: string, descorator: PropertyDescriptor) {
  descorator.get = function () {
    // @ts-ignore
    return `prefix_${this._name}`;
  };
}
class Test {
  _name: string = "xx";
  @prefix
  get name() {
    return this._name;
  }
  set name(name: string) {
    this._name = name;
  }
}
const test = new Test();
console.log(test.name); // 输出: prefix_xx

属性装饰器

declare type PropertyDecorator = (
  target: Object,
  propertyKey: string | symbol
) => void;

属性装饰器拥有两个参数:
1.target:当前对象的原型
2.propertyKey:属性的名称

参数装饰器

declare type ParameterDecorator = (
  target: Object,
  propertyKey: string | symbol,
  parameterIndex: number
) => void;

参数装饰器有三个参数:
1.target 当前对象的原型
2.method 当前方法的名称
3.paramIndex 当前参数对于的索引

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

柳晓黑胡椒

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值