购买
下载掌阅APP,畅读海量书库
立即打开
畅读海量书库
扫码下载掌阅APP

3.7 装饰器

TypeScript语言和ES6规范中都引入了“类”的概念,但在某些场景下可能需要使用某种标注来修改类及其成员。

装饰器(Decorator)提供了一种在类的声明及成员上通过元编程语法添加标注的方式。ES6规范没有“装饰器”的概念,而TypeScript是支持“装饰器”的。

提示:

如果你熟悉Java语言,那么对于装饰器就不会陌生。TypeScript中的装饰器类似于Java中的注解。

若要启用实验性的装饰器特性,则必须在命令行或tsconfig.json中启用experimentalDecorators编译器选项。

可以在命令行中启用装饰器选项,命令如下:

如果要在tsconfig.json中启用装饰器选项,则compilerOptions配置如下:

3.7.1 定义装饰器

装饰器是一种特殊类型的声明,它能够被附加到类声明、方法、访问符、属性或参数上。定义装饰器是使用“@表达式”这种形式的,其中表达式求值后必须为一个函数。它会在运行时被调用,被装饰的声明信息作为参数传入。

例如,有一个@sealed装饰器,我们会这样定义sealed()函数。

定义好装饰器后,就可以按下面的方式来使用@sealed装饰器。

3.7.2 了解装饰器的执行时机

多个装饰器可以同时应用到一个声明上。就像下面的示例,将多个装饰器书写在同一行上。

也可以书写在多行上。

当多个装饰器应用于一个声明上时,它们的求值方式与复合函数相似。在这种情况下,当复合first和second时,复合的结果等同于first(second(x))。

同样地,在TypeScript中,当多个装饰器应用于一个声明上时,会进行如下步骤的操作。

(1)由上至下依次对装饰器表达式求值。

(2)求值的结果会被当作函数,由下至上依次调用。

可以通过下面的例子来观察多个装饰器求值的顺序。

在控制台上会打印出如下结果:

类中不同声明上的装饰器的运行顺序不同。装饰器的运行顺序一般是:参数装饰器、方法装饰器、访问符装饰器、属性装饰器、类装饰器。

3.7.3 认识4类装饰器

在TypeScript中,常用的有以下4类装饰器。

1. 类装饰器

类装饰器声明在一个类声明之前(紧靠着类声明)。类装饰器应用于类的构造函数上,可以用来监视、修改或替换类定义。类装饰器不能用在声明文件(.d.ts)中,也不能用在任何外部上下文(比如declare的类)中。

类装饰器表达式会在运行时被当作函数调用,类的构造函数将作为其唯一的参数。

如果类装饰器返回一个值,那么它会使用提供的构造函数来替换类的声明。

下面是一个将类装饰器@sealed应用于DecoratorGreeter 类上的例子。

2. 方法装饰器

方法装饰器声明在一个方法声明之前(紧靠着方法声明)。它会被应用到方法的属性描述符上,可以用来监视、修改或者替换方法定义。方法装饰器不能用在声明文件(.d.ts)、重载或者任何外部上下文(比如declare的类)中。

方法装饰器表达式会在运行时被当作函数调用,传入下列3个参数。

● 对于静态成员来说是类的构造函数,对于实例成员来说是类的原型对象。

● 成员的名字。

● 成员的属性描述符。

下面是一个将装饰器@enumerable应用于DecoratorGreeter类上的例子。

可以用下面的函数来定义@enumerable装饰器。

这里的@enumerable(false)是一个装饰器工厂。当装饰器@enumerable(false)被调用时,它会修改属性描述符的enumerable属性。所谓装饰器工厂,是指以工厂模式来定义装饰器,其表现为可以在装饰器中传递参数。

3. 属性装饰器

属性装饰器声明在一个属性声明之前(紧靠着属性声明)。属性装饰器不能用在声明文件(.d.ts)或任何外部上下文(比如 declare的类)中。

属性装饰器表达式会在运行时被当作函数调用,传入下列两个参数。

● 对于静态成员来说是类的构造函数,对于实例成员来说是类的原型对象。

● 成员的名字。

注意

属性描述符不会被作为参数传入属性装饰器,这与TypeScript是如何初始化属性装饰器的有关。因为目前没有办法在定义一个原型对象的成员时描述一个实例属性,并且没有办法监视或修改一个属性的初始化方法。因此,属性描述符只能用来监视类中是否声明了某个名字的属性。

我们可以用属性装饰器来记录这个属性对应的元数据,如下例所示。

然后定义@format("Hello,%s")装饰器和getFormat()函数。

这里的@format("Hello,%s")是一个装饰器工厂。当装饰器@format("Hello,%s")被调用时,它会通过reflect-metadata库里的Reflect.metadata()函数添加一条greeting属性对应的元数据。当getFormat()函数被调用时,@format("Hello,%s")装饰器会读取到greeting属性所对应的格式化后的元数据。

4. 参数装饰器

参数装饰器声明在一个参数声明之前(紧靠着参数声明)。参数装饰器应用在类的构造函数或方法声明上。参数装饰器不能用在声明文件(.d.ts)、重载或其他外部上下文(比如 declare的类)中。

参数装饰器表达式会在运行时被当作函数调用,传入下列3个参数。

● 对于静态成员来说是类的构造函数,对于实例成员来说是类的原型对象。

● 成员的名字。

● 参数在函数参数列表中的索引。

下面定义了参数装饰器@required,并将其应用在DecoratorGreeter类方法的一个参数上。

然后使用下面的函数定义@required和@validate装饰器。

@required装饰器添加了元数据实体,把参数标记为必需的。@validate装饰器把greet()方法包裹在一个函数里,在调用原先的函数前验证函数参数。 7Bc7YJmFVkm5OWSFu9a1XWSYXU6ZS5s17OAWSkpQ0o+b3GHaJfvpIXzN2P+6bKZn

点击中间区域
呼出菜单
上一章
目录
下一章
×