TS 装饰器

Huy大约 2 分钟javascripttypescript

装饰器(Decorator)是一种语法结构,用来在定义时修改类(class)的行为。

在语法上,装饰器有如下几个特征。

(1)第一个字符(或者说前缀)是@,后面是一个表达式。

(2)@后面的表达式,必须是一个函数(或者执行后可以得到一个函数)。

(3)这个函数接受所修饰对象的一些相关值作为参数。

(4)这个函数要么不返回值,要么返回一个新对象取代所修饰的目标对象。

一般类有四个装饰器:

@frozen
class Foo {
  @configurable(false)
  @enumerable(true)
  method() {}

  @throttle(1000)
  expensiveMethod() {}
}

一个用在类本身(@frozen),另外三个用在类的方法(@configurable@enumerable@throttle)。

执行顺序

装饰器的执行分为两个阶段。

(1)评估(evaluation):计算@符号后面的表达式的值,得到的应该是函数。

(2)应用(application):将评估装饰器后得到的函数,应用于所装饰对象。

也就是说,装饰器的执行顺序是,先评估所有装饰器表达式的值,再将其应用于当前类。应用装饰器时,顺序依次为方法装饰器属性装饰器,然后是类装饰器

应用实例:

function d(str: string) {
  console.log(`评估 @d(): ${str}`)
  return (value: any, context: any) => console.log(`应用 @d(): ${str}`)
}

function log(str: string) {
  console.log(str)
  return str
}

@d('类装饰器')
class T {
  @d('静态属性装饰器')
  static staticField = log('静态属性值');

  @d('原型方法')
  [log('计算方法名')]() {}

  @d('实例属性')
  instanceField = log('实例属性值')
}

理想输出:

// "评估 @d(): 类装饰器"
// "评估 @d(): 静态属性装饰器"
// "评估 @d(): 原型方法"
// "计算方法名"
// "评估 @d(): 实例属性"
// "应用 @d(): 原型方法"
// "应用 @d(): 静态属性装饰器"
// "应用 @d(): 实例属性"
// "应用 @d(): 类装饰器"
// "静态属性值"

但是经过笔者测试用 deno 运行后结果为:

计算方法名
静态属性值
评估 @d(): 静态属性装饰器
应用 @d(): 静态属性装饰器
评估 @d(): 原型方法
应用 @d(): 原型方法
评估 @d(): 实例属性
应用 @d(): 实例属性
评估 @d(): 类装饰器
应用 @d(): 类装饰器

原因可为 deno 采用的是旧版 typescript 编译器,而旧版编译器在评估装饰器时,会先将装饰器应用到当前类,然后再评估表达式。

参考文件

Typescript 装饰器open in new window

Loading...