接上篇。
这篇主要记录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)),先执行下面的装饰器后再执行上面的。
若多种装饰器共存是,其执行顺序是:属性装饰器 → 方法装饰器 → 方法参数装饰器 → 类装饰器。