这是我参与 8 月更文挑战的第 17 天,活动详情查看: 8月更文挑战
前言
本章内容我们主要来聊聊TS中的装饰器,装饰器是一种特殊类型的声明,它能够被附加到类声明,方法, 访问符,属性或参数上。 装饰器使用 @expression这种形式,expression求值后必须为一个函数,它会在运行时被调用,被装饰的声明信息做为参数传入。
为什么要使用装饰器?
装饰器也是一种设计模式。在日常开发中,我们有时总有这样的诉求:在函数执行前,先进行一个类型校验、数据过滤、调用次数的限制等操作,对于函数,属于业务代码,直接改造不适合,那么是否可以加入一个中间层,来帮忙处理一些比较特殊的逻辑呢?那么,装饰器就是首选。
初探TS装饰器
我们先来看下装饰器的语法:
function helloWord(target: any) {
console.log('hello Word!');
}
@helloWord
class HelloWordClass {
}
在执行后,会立即输出hello Word!,装饰器函数是一种自执行函数。但是,这种自执行是在编译期间完成的,而不是在运行时。这意味着,修饰器能在编译阶段运行代码。
方法装饰器
方法装饰器声明在一个方法的声明之前(紧靠着方法声明)。 它会被应用到方法的 属性描述符上,可以用来监视,修改或者替换方法定义。 方法装饰器不能用在声明文件( .d.ts),重载或者任何外部上下文(比如declare的类)中。
方法装饰器表达式会在运行时当作函数被调用,传入下列3个参数:
- 对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。
- 成员的名字。
- 成员的属性描述符。
class Greeter {
greeting: string;
constructor(message: string) {
this.greeting = message;
}
@enumerable(false)
greet() {
return "Hello, " + this.greeting;
}
}
访问器装饰器
访问器装饰器声明在一个访问器的声明之前(紧靠着访问器声明)。 访问器装饰器应用于访问器的 属性描述符并且可以用来监视,修改或替换一个访问器的定义。 访问器装饰器不能用在声明文件中(.d.ts),或者任何外部上下文(比如 declare的类)里。
class Point {
private _x: number;
private _y: number;
constructor(x: number, y: number) {
this._x = x;
this._y = y;
}
@configurable(false)
get x() { return this._x; }
@configurable(false)
get y() { return this._y; }
}
属性装饰器
属性装饰器声明在一个属性声明之前(紧靠着属性声明)。 属性装饰器不能用在声明文件中(.d.ts),或者任何外部上下文(比如 declare的类)里。
class Greeter {
@format("Hello, %s")
greeting: string;
constructor(message: string) {
this.greeting = message;
}
greet() {
let formatString = getFormat(this, "greeting");
return formatString.replace("%s", this.greeting);
}
}
bable配置
若要启用实验性的装饰器特性,你必须在命令行或tsconfig.json里启用experimentalDecorators编译器选项:
tsconfig.json:
{
"compilerOptions": {
"target": "ES5",
"experimentalDecorators": true
}
}
小结
基于我们的理论和实践,我们可以得出以下结论:
-
对于多个参数装饰器的执行顺序:从最后一个参数开始,但是类装饰器总是最后执行。
-
方法和方法参数中参数装饰器先执行。
-
方法和属性装饰器,谁在前面谁先执行。因为参数属于方法一部分,所以参数会一直紧紧挨着方法执行。