1. 程式人生 > >TypeScript學習筆記(九):裝飾器(Decorators)

TypeScript學習筆記(九):裝飾器(Decorators)

標註 時裝 als cal () 操作 enume 筆記 文檔

裝飾器簡介

裝飾器(Decorators)為我們在類的聲明及成員上通過元編程語法添加標註提供了一種方式。

需要註意的是:裝飾器是一項實驗性特性,在未來的版本中可能會發生改變。

若要啟用實驗性的裝飾器特性,你必須在命令行或tsconfig.json裏啟用experimentalDecorators編譯器選項:

1 {
2     "compilerOptions": {
3         "target": "ES5",
4         "experimentalDecorators": true
5     }
6 }

如何定義

[email protected]

/* */,expression求值後必須為一個函數,它會在運行時被調用,被裝飾的聲明信息做為參數傳入。

裝飾器組合

多個裝飾器可以同時應用到一個聲明上,就像下面的示例:

書寫在同一行上:

@f @g x

書寫在多行上:

@f
@g
x

在TypeScript裏,當多個裝飾器應用在一個聲明上時會進行如下步驟的操作:

  1. 由上至下依次對裝飾器表達式求值。
  2. 求值的結果會被當作函數,由下至上依次調用。

類裝飾器

類裝飾器在類聲明之前被聲明(緊靠著類聲明)。 類裝飾器應用於類構造函數,可以用來監視,修改或替換類定義。 類裝飾器不能用在聲明文件中( .d.ts),也不能用在任何外部上下文中(比如declare的類)。

類裝飾器表達式會在運行時當作函數被調用,類的構造函數作為其唯一的參數。

我們來看一個例子:

 1 function sealed(constructor: Function) {
 2     Object.seal(constructor);
 3     Object.seal(constructor.prototype);
 4 }
 5 
 6 @sealed
 7 class MyClass {
 8     a: number = 0;
 9     b: string = "hello";
10 }
11 
12 var obj = new MyClass();
13 // obj.c = true; // 編譯報錯

通過類裝飾器我們可以對類的原型對象做一定的修改。

方法裝飾器

方法裝飾器聲明在一個方法的聲明之前(緊靠著方法聲明)。 它會被應用到方法的 屬性描述符上,可以用來監視,修改或者替換方法定義。 方法裝飾器不能用在聲明文件( .d.ts),重載或者任何外部上下文(比如declare的類)中。

方法裝飾器表達式會在運行時當作函數被調用,傳入下列3個參數:

  1. 對於靜態成員來說是類的構造函數,對於實例成員是類的原型對象。
  2. 成員的名字。
  3. 成員的屬性描述符。

註意:如果代碼輸出目標版本小於ES5,屬性描述符將會是undefined。

我們來看下面的例子:

 1 function methodDecorator(param1: boolean, param2?: string) {
 2     return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
 3         console.log(param1 + ", " + param2 + ", " + target + ", " + propertyKey + ", " + JSON.stringify(descriptor));
 4     };
 5 }
 6 
 7 class MyClass {
 8     @methodDecorator(true, "this is static")
 9     public static sFunc(): void {
10         console.log("call static method");
11     }
12 
13     @methodDecorator(false)
14     public func(): void {
15         console.log("call method");
16     }
17 }
18 
19 MyClass.sFunc();
20 MyClass.sFunc();
21 
22 var obj = new MyClass();
23 obj.func();
24 obj.func();

輸出如下:

1 false, undefined, [object Object], func, {"writable":true,"enumerable":true,"configurable":true}
2 true, this is static, function MyClass() {
3     }, sFunc, {"writable":true,"enumerable":true,"configurable":true}
4 call static method
5 call static method
6 call method
7 call method

我們可以發現,方法裝飾器返回的函數會在解釋類的對應方法時被調用一次,並可以得到裝飾器的參數和被裝飾的方法的相關信息。

裝飾器方法的調用只會在加載代碼時執行一次,調用被裝飾的方法不會觸發裝飾器方法。

訪問器裝飾器

訪問器裝飾器聲明在一個訪問器的聲明之前(緊靠著訪問器聲明)。 訪問器裝飾器應用於訪問器的 屬性描述符並且可以用來監視,修改或替換一個訪問器的定義。 訪問器裝飾器不能用在聲明文件中(.d.ts),或者任何外部上下文(比如 declare的類)裏。

訪問器裝飾器表達式會在運行時當作函數被調用,傳入下列3個參數:

  1. 對於靜態成員來說是類的構造函數,對於實例成員是類的原型對象。
  2. 成員的名字。
  3. 成員的屬性描述符。

註意:如果代碼輸出目標版本小於ES5,Property Descriptor將會是undefined。

同時TypeScript不允許同時裝飾一個成員的get和set訪問器。取而代之的是,一個成員的所有裝飾的必須應用在文檔順序的第一個訪問器上。這是因為,在裝飾器應用於一個屬性描述符時,它聯合了get和set訪問器,而不是分開聲明的。

我們來看一個例子:

 1 function methodDecorator(param1: boolean, param2?: string) {
 2     return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
 3         console.log(param1 + ", " + param2 + ", " + target + ", " + propertyKey + ", " + JSON.stringify(descriptor));
 4     };
 5 }
 6 
 7 class MyClass {
 8     private static _myName: string;
 9 
10     @methodDecorator(true, "this is static")
11     public static set myName(value: string) {
12         this._myName = value;
13     }
14 
15     public static get myName(): string {
16         return this._myName;
17     }
18 
19     private _age: number;
20 
21     @methodDecorator(false)
22     public set age(value: number) {
23         this._age = value;
24     }
25 
26     public get age(): number {
27         return this._age;
28     }
29 }
30 
31 MyClass.myName = "hello";
32 console.log(MyClass.myName);
33 
34 var obj = new MyClass();
35 obj.age = 28;
36 console.log(obj.age);

輸出如下:

1 false, undefined, [object Object], age, {"enumerable":true,"configurable":true}
2 true, this is static, function MyClass() {
3     }, myName, {"enumerable":true,"configurable":true}
4 hello
5 28

我們可以發現,訪問器裝飾器返回的函數會在解釋類的對應訪問器時被調用一次,並可以得到裝飾器的參數和被裝飾的訪問器的相關信息。

裝飾器方法的調用只會在加載代碼時執行一次,調用被裝飾的訪問器不會觸發裝飾器方法。

屬性裝飾器

屬性裝飾器聲明在一個屬性聲明之前(緊靠著屬性聲明)。 屬性裝飾器不能用在聲明文件中(.d.ts),或者任何外部上下文(比如 declare的類)裏。

屬性裝飾器表達式會在運行時當作函數被調用,傳入下列2個參數:

  1. 對於靜態成員來說是類的構造函數,對於實例成員是類的原型對象。
  2. 成員的名字。

我們來看一個例子:

 1 function propDecorator(param1: boolean, param2?: string) {
 2     return function (target: any, propertyKey: string) {
 3         console.log(param1 + ", " + param2 + ", " + target + ", " + propertyKey);
 4     };
 5 }
 6 
 7 class MyClass {
 8     @propDecorator(false, "Hi")
 9     public static A: number = 0;
10 
11     @propDecorator(true)
12     public a: string = "hello";
13 }
14 
15 console.log(MyClass.A);
16 var obj = new MyClass();
17 console.log(obj.a);

輸出如下:

1 true, undefined, [object Object], a
2 false, Hi, function MyClass() {
3         this.a = "hello";
4     }, A
5 0
6 hello

我們可以發現,屬性裝飾器返回的函數會在解釋類的對應屬性時被調用一次,並可以得到裝飾器的參數和被裝飾的屬性的相關信息。

裝飾器方法的調用只會在加載代碼時執行一次,調用被裝飾的屬性不會觸發裝飾器方法。

參數裝飾器

參數裝飾器聲明在一個參數聲明之前(緊靠著參數聲明)。 參數裝飾器應用於類構造函數或方法聲明。 參數裝飾器不能用在聲明文件(.d.ts),重載或其它外部上下文(比如 declare的類)裏。

參數裝飾器表達式會在運行時當作函數被調用,傳入下列3個參數:

  1. 對於靜態成員來說是類的構造函數,對於實例成員是類的原型對象。
  2. 成員的名字。
  3. 參數在函數參數列表中的索引。

我們來看一個例子:

 1 function paramDecorator(target: Object, propertyKey: string | symbol, parameterIndex: number) {
 2     console.log(target + ", " + <any> propertyKey + ", " + parameterIndex);
 3 }
 4 
 5 class MyClass {
 6     public func(@paramDecorator a: number, @paramDecorator b: string = "hello", @paramDecorator c?: boolean): void {
 7         console.log("call method");
 8     }
 9 }
10 
11 var obj = new MyClass();
12 obj.func(1);
13 obj.func(2);

輸出如下:

1 [object Object], func, 2
2 [object Object], func, 1
3 [object Object], func, 0
4 call method
5 call method

我們可以發現,參數裝飾器返回的函數會在解釋方法的參數時被調用一次,並可以得到參數的相關信息。我們有3個參數就調用了3次。

裝飾器方法的調用只會在加載代碼時執行一次,調用被裝飾的參數的方法不會觸發裝飾器方法。

TypeScript學習筆記(九):裝飾器(Decorators)