# 简介
装饰器是一种语法结构,用来在定义时修改类的行为。
在语法上,装饰器有如下几个特征。
- 第一个字符(或者说前缀)是@,后面是一个表达式。
- @后面的表达式,必须是一个函数(或者执行后可以得到一个函数)。
- 这个函数接受所修饰对象的一些相关值作为参数。
- 这个函数要么不返回值,要么返回一个新对象取代所修饰的目标对象。
// 装饰器有多种形式,基本上只要在@符号后面添加表达式都是可以的
@myFunc
@myFuncFactory(arg1, arg2)
@libraryModule.prop
@someObj.method(123)
@(wrap(dict['prop']))
2
3
4
5
6
相比使用子类改变父类,装饰器更加简洁优雅,缺点是不那么直观,功能也受到一些限制。所以,装饰器一般只用来为类添加某种特定行为。
@frozen class Foo { // 一个用在类本身
@configurable(false) //三个用在类的方法
@enumerable(true)
method() {}
@throttle(500)
expensiveMethod() {}
}
2
3
4
5
6
7
# 装饰器的版本
标准语法可以直接使用,传统语法需要打开--experimentalDecorators编译参数。
tsc --target ES5 --experimentalDecorators
# 装饰器的结构
Decorator是装饰器的类型定义。它是一个函数,使用时会接收到value和context两个参数。
- value:所装饰的对象。
- context:上下文对象,TS提供一个原生接口ClassMethodDecoratorContext,描述这个对象。
- context对象的属性,根据所装饰对象的不同而不同,其中只有两个属性(kind和name)是必有的,其他都是可选的。
- kind:字符串,表示所装饰对象的类型。
- name:字符串或者 Symbol 值,所装饰对象的名字,比如类名、属性名等。
- addInitializer():函数,用来添加类的初始化逻辑。在类完全定义结束后执行。
- private:布尔值,表示所装饰的对象是否为类的私有成员。
- static:布尔值,表示所装饰的对象是否为类的静态成员。
- access:一个对象,包含了某个值的 get 和 set 方法。
- context对象的属性,根据所装饰对象的不同而不同,其中只有两个属性(kind和name)是必有的,其他都是可选的。
# 类装饰器
类装饰器接受两个参数: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;
}
}
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)