接上篇。html
這篇主要記錄TypeScript中的幾種裝飾器的概念與用法。裝飾器(Decorators):用一種特性標註的寫法做爲聲明,可以給類,方法,屬性擴展功能,能夠簡單地理解爲是非侵入式的行爲修改。分爲:類裝飾器、方法裝飾器、屬性裝飾器、訪問器裝飾器、參數裝飾器。json
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)),先執行下面的裝飾器後再執行上面的。ui
若多種裝飾器共存是,其執行順序是:屬性裝飾器 → 方法裝飾器 → 方法參數裝飾器 → 類裝飾器。this