这篇主要记录TypeScript中的几种装饰器的概念与用法。装饰器(Decorators):用一种特性标注的写法作为声明,能够给类,方法,属性扩展功能,可以简单地理解为是非侵入式的行为修改。分为:类装饰器、方法装饰器、属性装饰器、访问器装饰器、参数装饰器。
类装饰器
1 /**
2 * 装饰器(Decorators):用一种特性标注的写法作为声明,能够给类,方法,属性扩展功能,可以简单地理解为是非侵入式的行为修改
3 * 分为:类装饰器、方法装饰器、属性装饰器、访问器装饰器、参数装饰器
4 * 注:在tsconfig.json中将打开 “"experimentalDecorators": true” 以启用装饰器功能
5 */
6
7 /* ****************************类装饰器******************************* */
8 //以下这个Person类使用了装饰器“yasuo”
9 //@yasuo
10 class Person {
11 roleLine: string | undefined;
12
13 constructor(readonly name: string, readonly birthDay: Date) {
14 this.roleLine = "Hello everyone"
15 }
16
17 SayHi(): void {
18 console.log(this.roleLine);
19 }
20 }
21
22 /**
23 * 装饰器其实也是一个方法,装饰器中可以做的事有三:
24 * (1)做点自己想做的事
25 * (2)扩展属性或者方法
26 * (3)重载类的构造函数
27 */
28
29 //参数target便代表Person类(的构造器)
30 function yasuo(target: any) {
31 //(1)只要有类应用了这个装饰器,便会执行
32 console.log("Follow the wind, but watch your back.")
33
34 //(2)为Person类扩展一个Attach的方法
35 target.prototype.Attack = function () {
36 console.log("Hasaki!");
37 }
38
39 //(3)重载构造函数
40 return class extends target {
41 roleLine: any = "Make it quick."
42 }
43 }
44 //这里指定为any类型是为了防止调用扩展方法Attack时TypeScript编译器报错
45 let hero: any = new Person("yasuo", new Date("2013-12-14"));
46
47 hero.SayHi(); // 控制台输出:Make it quick.
48 //hero.Attack() // 控制台输出:Hasaki!
49
50
51 /* ****************************装饰器工厂****************************** */
52 // 带参数的装饰器
53 @skin("黑夜使者")
54 class Hero {
55 roleLine: string | undefined;
56
57 constructor(readonly name: string, readonly birthDay: Date) {
58 this.roleLine = "welcome to LOL"
59 }
60
61 SayHi(): void {
62 console.log(this.roleLine);
63 }
64 }
65 //定义“装饰器工厂”
66 function skin(value: string) { // 这是一个装饰器工厂
67 return function (target: any) { // 这是装饰器
68 let line: string;
69 switch (value) {
70 case "黑夜使者":
71 line = "我是变革之风"
72 break;
73 case "猩红之月":
74 line = "Hasaki"
75 break;
76 default:
77 line = "Attack"
78 break;
79 }
80
81 target.prototype.Attack = function () {
82 console.log(line);
83 }
84 }
85 }
86
87 let h1: any = new Hero("yasuo", new Date("2013-12-14"));
88 h1.Attack(); //控制台输出:我是变革之风
属性装饰器
1 /* ****************************属性装饰器****************************** */
2 // 属性装饰器接收两个参数
3 // 参数一:对于静态成员是构造器,对于实例成员是类的原型对象
4 // 参数二:属性的名称
5
6 class Hero {
7
8 @lineInjection("Hasaki") //使用装饰器为其赋值
9 roleLine: string | undefined;
10
11 constructor(readonly name: string, readonly birthDay: Date) {
12 }
13
14 SayHi(): void {
15 console.log(this.roleLine);
16 }
17 }
18
19 //定义一个属性装饰器
20 function lineInjection(line: string) { //装饰器的参数
21 return function (target: any, prop: any) {
22 target[prop] = line;
23 }
24 }
25
26 let h1 = new Hero("yasuo", new Date("2013-12-14"));
27 h1.SayHi(); //控制台输出:Hasaki
方法装饰器
1 /* ****************************方法装饰器****************************** */
2 // 用于监视,修改方法,接受3个参数
3 // 参数一:对于静态成员是构造器,对于实例成员是类的原型对象
4 // 参数二:方法的名称
5 // 参数三:方法的属性描述符,其中value属性就是当前方法的定义
6
7 class Person {
8 constructor(public firstName: string, public lastName: string) { }
9
10 @useFullName
11 public SayHi(): void {
12 console.log(`i am ${this.firstName}`);
13 }
14 }
15
16 //定义一个方法装饰器,修改方法,让其输出全名
17 function useFullName(
18 target: object, //参数一:类的原型对象
19 propertyName: string, //参数二:成员的名称
20 descriptor: PropertyDescriptor //参数三:成员的描述信息
21 ): void {
22
23 //将改写前的原方法暂存下来
24 const sayLastName: Function = descriptor.value;
25
26 //改写方法
27 descriptor.value = function () {
28 //先调用一次原方法
29 sayLastName.call(this);
30 //再输出一次全名
31 console.log(`${this.firstName} ${this.lastName}`)
32 };
33 }
34
35 let mj = new Person("bond", "james");
36
37 mj.SayHi(); //控制台输出:i am bond
38 //bond james
方法参数装饰器
1 /* ****************************方法参数装饰器****************************** */
2 // 可以用来监视方法传入的参数
3 // 和之前几个装饰器类似,接受的参数如下
4 // 参数一:对于静态成员是构造器,对于实例成员是类的原型对象
5 // 参数二:方法的名称
6 // 参数三:参数在方法中的位置(index)
7
8 class Greeter {
9 greeting: string;
10
11 constructor(message: string) {
12 this.greeting = message;
13 }
14
15 greet(@log name: string) {
16 return "Hello " + name + ", " + this.greeting;
17 }
18 }
19
20 function log(
21 target: Object,
22 propertyKey: string,
23 parameterIndex: number) {
24
25 console.log(target);
26 console.log(propertyKey);
27 console.log(parameterIndex);
28 }
装饰器的执行顺序
如果一个对象中包含多个装饰器,如
1 @f
2 @g
3 class x
那么他的执行顺序是f(g(x)),先执行下面的装饰器后再执行上面的。
若多种装饰器共存是,其执行顺序是:属性装饰器 → 方法装饰器 → 方法参数装饰器 → 类装饰器。