从一个小例子学习Typescript中的装饰器Decorator

371 阅读2分钟

在ECMAScript中,很早就存在关于装饰器Decorator的提案,但一直没有作为正式的特性发布到Javascript中。Typescript实现了其中的一种版本,供我们使用和学习。不过没关系,区别仅仅在于实现的细节,只要我们理解了装饰器的目的和作用,即使有了新版本的规范,我们也能从容面对。


装饰器是用来更改Class的行为,通过装饰器,我们可以改变某个类的属性、方法、构造函数的参数等等。而装饰器的本质就是一个函数,我们在使用时,会把类及其属性传给该函数,然后在函数内部更改传入的类或类的方法,即可实现我们的目的。


以常见的函数节流功能为例,可以编写一个节流装饰器工厂函数:

function throttle(timeout: number) {
  return function (target: any, propertyName: string, descriptor: any) {
    const originalValue = descriptor.value;
    let enable = true;
    return {
      ...descriptor,
      value: function(...args: any[]) {
        if (!enable) { return; }
        enable = false;
        originalValue(args);
        setTimeout(() => {
          enable = true;
        }, timeout);
      }
    };
  };
}

在上述工厂函数中,我们接收一个参数,来确定节流的时间间隔。然后我们在工厂函数中返回一个函数,该函数就是实际的装饰器函数。

装饰器函数接收三个参数:

  • 我们作用的方法所在类的构造函数或者原型(这取决于方法是静态方法还是实例方法)
  • 方法名
  • 该方法属性的描述符

然后我们在装饰器函数中返回了一个描述符对象,重写了我们将要更改的方法的描述符。我们把描述符的value替换成了我们新写的具有节流功能的函数。

然后就可以使用这个修饰符了。

class B {
  constructor() {}
  @throttle(1100)
  alert(string: string) {
    console.log(new Date().getUTCSeconds());
    console.log(string);
  }
}
let ab = new B();
setInterval(() => {
  ab.alert('报警');
}, 400);

我们会发现,即使我们设置了每隔400ms打印一次信息,实际上是每隔1100ms才会打印一次。

上述例子可以从这里运行:https://www.typescriptlang.org/play