装饰器

2024/4/8 TypeScript

# 简介

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

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

  • 第一个字符(或者说前缀)是@,后面是一个表达式。
  • @后面的表达式,必须是一个函数(或者执行后可以得到一个函数)。
  • 这个函数接受所修饰对象的一些相关值作为参数。
  • 这个函数要么不返回值,要么返回一个新对象取代所修饰的目标对象。
// 装饰器有多种形式,基本上只要在@符号后面添加表达式都是可以的
@myFunc
@myFuncFactory(arg1, arg2)
@libraryModule.prop
@someObj.method(123)
@(wrap(dict['prop']))
1
2
3
4
5
6

​ 相比使用子类改变父类,装饰器更加简洁优雅,缺点是不那么直观,功能也受到一些限制。所以,装饰器一般只用来为类添加某种特定行为。

@frozen class Foo { // 一个用在类本身
  @configurable(false) //三个用在类的方法
  @enumerable(true)
  method() {}
  @throttle(500)
  expensiveMethod() {}
}
1
2
3
4
5
6
7

# 装饰器的版本

​ 标准语法可以直接使用,传统语法需要打开--experimentalDecorators编译参数。

tsc --target ES5 --experimentalDecorators
1

# 装饰器的结构

​ Decorator是装饰器的类型定义。它是一个函数,使用时会接收到value和context两个参数。

  • value:所装饰的对象。
  • context:上下文对象,TS提供一个原生接口ClassMethodDecoratorContext,描述这个对象。
    • context对象的属性,根据所装饰对象的不同而不同,其中只有两个属性(kind和name)是必有的,其他都是可选的。
      • kind:字符串,表示所装饰对象的类型。
      • name:字符串或者 Symbol 值,所装饰对象的名字,比如类名、属性名等。
      • addInitializer():函数,用来添加类的初始化逻辑。在类完全定义结束后执行。
      • private:布尔值,表示所装饰的对象是否为类的私有成员。
      • static:布尔值,表示所装饰的对象是否为类的静态成员。
      • access:一个对象,包含了某个值的 get 和 set 方法。

# 类装饰器

​ 类装饰器接受两个参数:value(当前类本身)和context(上下文对象)。其中,context对象的kind属性固定为字符串class。

​ 类装饰器一般用来对类进行操作,可以不返回任何值。

​ 类装饰器可以返回一个函数,替代当前类的构造方法。

​ 类装饰器也可以返回一个新的类,替代原来所装饰的类。

# 方法装饰器

​ 方法装饰器用来装饰类的方法(method)。方法装饰器会改写类的原始方法(.prototype上的)。

​ 方法装饰器是一个函数,接受两个参数:value和context。参数value是方法本身,参数context是上下文对象,有以下属性。

  • kind:值固定为字符串method,表示当前为方法装饰器。
  • name:所装饰的方法名,类型为字符串或 Symbol 值。
  • static:布尔值,表示是否为静态方法。该属性为只读属性。
  • private:布尔值,表示是否为私有方法。该属性为只读属性。
  • access:对象,包含了方法的存取器,但是只有get()方法用来取值,没有set()方法进行赋值。
  • addInitializer():为方法增加初始化函数。一个钩子方法,用来在类的初始化阶段添加回调函数。

​ 如果方法装饰器返回一个新的函数,就会替代所装饰的原始函数。

​ 利用方法装饰器,可以将类的方法变成延迟执行。

# 属性装饰器

​ 属性装饰器用来装饰定义在类顶部的属性(field)。

注意,装饰器的第一个参数value的类型是undefined,这意味着这个参数实际上没用的,装饰器不能从value获取所装饰属性的值。另外,第二个参数context对象的kind属性的值为字符串field,而不是“property”或“attribute”,这一点是需要注意的。

​ 属性装饰器要么不返回值,要么返回一个函数,该函数会自动执行,用来对所装饰属性进行初始化。该函数的参数是所装饰属性的初始值,该函数的返回值是该属性的最终值。

​ 属性装饰器的返回值函数,可以用来更改属性的初始值。

​ 属性装饰器的上下文对象context的access属性,提供所装饰属性的存取器。

# getter 装饰器,setter 装饰器

​ 属性装饰器的上下文对象context的access属性,提供所装饰属性的存取器。

注意,getter 装饰器的上下文对象context的access属性,只包含get()方法;setter 装饰器的access属性,只包含set()方法。

​ 这两个装饰器要么不返回值,要么返回一个函数,取代原来的取值器或存值器。

# accessor 装饰器

class C {
  accessor x = 1; // accessor等同于为公开属性x自动生成取值器和存值器,它们作用于私有属性x
} 
//公开的x与私有的x不是同一个。也就是说,上面的代码等同于下面的代码。
class C {
  #x = 1;
  get x() {
    return this.#x;
  }
  set x(val) {
    this.#x = val;
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13

​ accessor也可以与静态属性和私有属性一起使用。

​ accessor 装饰器的value参数,是一个包含get()方法和set()方法的对象。

​ 该装饰器可以不返回值,或者返回一个新的对象,用来取代原来的get()方法和set()方法。

​ 此外,装饰器返回的对象还可以包括一个init()方法,用来改变私有属性的初始值。

# 装饰器的执行顺序

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

  • 评估(evaluation):计算@符号后面的表达式的值,得到的应该是函数。
  • 应用(application):将评估装饰器后得到的函数,应用于所装饰对象。

​ 也就是说,装饰器的执行顺序是,先评估所有装饰器表达式的值,再将其应用于当前类。

​ 应用装饰器时,顺序依次为方法装饰器和属性装饰器,然后是类装饰器。如果一个方法或属性有多个装饰器,则内层的装饰器先执行,外层的装饰器后执行。

# 装饰器(旧语法)

装饰器(旧语法) - TypeScript 教程 - 网道 (wangdoc.com) (opens new window)

上次更新: 2024/4/13 07:03:33