TypeScript 裝飾器

TypeScript 與 ES6 的裝飾器?

  1. ES6 的裝飾器是一種函數,寫成@ + 函數名。它可以放在類和類方法的定義前面。
  2. TypeScript 的裝飾器是一種特殊類型的聲明,它能夠被附加到類、方法、訪問符、屬性和參數上。裝飾器使用 @expression 這種形式,expression 求值后必須是一個函數,會在運行時被調用,被裝飾的聲明信息作為參數傳入。

注意:裝飾器是一項實驗性特性,在未來的版本中可能會發生變化

在 TypeScript 中不能直接使用,必須要啟用實驗性裝飾器,可以在命令行或者 tsconfig.json 里啟用 experimentalDecoratiors 編譯器選項:

  1. 命令行
tsc --target ES5 --experimentalDecorators
  1. tsconfig.json
{
    "compilerOptions": {
        "target": "ES5",
        "experimentalDecorators": true
    }
}

裝飾器只能裝飾類里面的東西,因為函數存在聲明的提升,所以不能裝飾單個函數。

一、裝飾器工廠

一個最簡單的裝飾器是 @ + 函數名,例如 sealed 裝飾器,定義 sealed 函數如下:

function sealed(target:any) {
    console.log(target);
}

@sealed
class A{}

這個裝飾器缺點特別的明顯,我們無法傳入參數,所以出現了裝飾器工廠,裝飾器工廠就是個高階函數,函數返回函數,利于我們傳入參數。

function color(params: string) { // 這是裝飾器工廠
    return function(target:any) { // 這是裝飾器
        target.prototype.colorName = params;
        console.log(params);
        console.log(target);
    }
}

@color("red")
class Color{
    colorName:string = "blue";
    constructor(){

    }
};

二、組合裝飾器

多個裝飾器可以同時應用到一個聲明上,比如:

書寫在同一行上:

@f @g x

書寫在多行上(推薦):

@f
@g
x

當多個裝飾器應用在同一個聲明上的時候,裝飾器的執行順序為:

  1. 如果不是裝飾器工廠的話,執行順序由下往上。
  function sigle1(target:any) {
    console.log(1);
}

function sigle2(target:any) {
    console.log(2);
}

function sigle3(target:any) {
    console.log(3);
}

@sigle1
@sigle2
@sigle3
class A {

}
//執行結果321
  1. 如果是裝飾器工廠的話,先執行完工廠函數,然后再去由下而上執行裝飾器函數。
function f() {
    console.log("1");
    return function (target:any, propertyKey: string, descriptor: PropertyDescriptor) {
        console.log("2");
    }
}

function g() {
    console.log("3");
    return function (target:any, propertyKey: string, descriptor: PropertyDescriptor) {
        console.log("4");
    }
}

function h() {
    console.log("5");
    return function (target:any, propertyKey: string, descriptor: PropertyDescriptor) {
        console.log("6");
    }
}
class C {
    @f()
    @g()
    @h()
    method() {}
}
//執行結果為135642

三、類裝飾器

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

function color(params: string) { // 這是裝飾器工廠
    return function(target:any) { // 這是裝飾器
        target.prototype.colorName = params;
        target.prototype.run = ()=>{};
        console.log(params);
        console.log(target);
    }
}

@color("red")
class Color{
    colorName:string = "blue";
    constructor(){

    }
};

可以在不修改原類的情況下,動態修改類的屬性和方法。甚至還可以返回一個類,例如:

function color(target: any) { // 這是裝飾器
    return class A extends target {
        method(){
            console.log("A")
        }
    }
}

@color
class Color{
    method(){
        console.log("Color");
    }
    getData(){

    }
};


四、方法裝飾器

方法裝飾器 :聲明在一個方法之前(緊靠著方法聲明)。它會被應用到方法的 屬性描述符 上,可以用來監視、修改或者替換方法定義。

方法裝飾器不能用在聲明文件(.d.ts),重載或者任何外部上下文(比如 declate 類)中。

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

對于靜態成員來說是類的構造函數,對于實例成員來說是類的原型對象
成員的名字
成員的 屬性描述符

注意:如果代碼輸出目標版本小于 ES5,屬性描述符是 undefined,如果代碼輸出目標版本小雨 ES5,屬性返回值會被忽略。

如果方法裝飾器返回一個值,他會被用作方法的 屬性描述符。

下面是一個方法裝飾器 @enumerable 的例子,應用到 Greeter 類的方法上:

function enumerable(value: boolean) {
    return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
        console.log(target,propertyKey,descriptor);
        console.log("裝飾的函數"+descriptor.value);
        descriptor.enumerable = value;
    };
}

class Greeter {
    greeting: string;
    constructor(message: string) {
        this.greeting = message;
    }

    @enumerable(false)
    greet() {
        return "Hello, " + this.greeting;
    }
}

這里的 @enumerable(false) 是一個裝飾工廠,當裝飾器 @enumerable(false) 被調用的時候,它會修改屬性描述符的 enumerable 屬性。

五、參數裝飾器

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

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

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

注意: 參數裝飾器只能用來監視一個方法的參數是否被傳入。

下例定義了參數裝飾器(@getParams)并應用于 A 類方法的一個參數:


function getParams(value: boolean) {
    return function (target: any, name: string, index: number) {
        console.log(target,name,index);
    };
}

class A {
    greeting: string;
    constructor(message: string) {
        this.greeting = message;
    }
    greet(@getParams(false)str:string) {
        return "Hello, " + this.greeting;
    }
}

六、屬性裝飾器

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

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

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

注意? 屬性描述符 不會做為參數傳入屬性裝飾器,這與 TypeScrip t是如何初始化屬性裝飾器的有關。
因為目前沒有辦法在定義一個原型對象的成員時描述一個實例屬性,并且沒辦法監視或修改一個屬性的初始化方法。
返回值也會被忽略。
因此,屬性描述符只能用來監視類中是否聲明了某個名字的屬性。

function getProperty(value: boolean) {
    return function (target: any, name: string) {
        console.log(target,name);
    };
}

class A {
    @getProperty(false)
    greeting: string;
    constructor(message: string) {
        this.greeting = message;
    }
}

七、訪問符裝飾器

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

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

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

  1. 對于靜態成員是類的構造函數,對于實例成員是類的原型對象
  2. 成員的名字
  3. 成員的 屬性描述符
    注意:如果代碼輸出目標版本小于 ES5, property descriptor 成員屬性描述符將會是 undefined

如果訪問器返回一個值,它會被用作方法的 屬性描述符。

注意:如果代碼輸出目標版本小于 ES5,返回值會被忽略

function getProperty(value: boolean) {
    return function (target: any, name: string) {
        console.log(target,name);
    };
}

function configurable(val: boolean) {
    return function (target: any, name: string, desc: PropertyDescriptor) {
        console.log(target,name , desc);
    };
}

class A {
    @getProperty(false)
    greeting: string;
    constructor(message: string) {
        this.greeting = message;
    }
    
    @configurable(false)
    get props(){return 111}
    set props(value:any){return value}

}

八、裝飾器求值順序

裝飾器的執行順序:

function a(target:any) {
    console.log(1)
}

function b(target:any,name:string) {
    console.log(2)
}
function c(target:any,name:string) {
    console.log(3)
}
function d(target:any,name:string) {
    console.log(4)
}
function e(target:any,name:string,index:number) {
    console.log(5)
}

@a
class A {
    @b
    greeting: string;
    constructor(message: string) {
        this.greeting = message;
    }
    @d
    method(@e str:string){
        
    }
    @c
    get props(){return 111}

}
//25431

屬性裝飾器<= 參數裝飾器<=方法裝飾器<=訪問符裝飾器<=類裝飾器

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 什么是裝飾器 裝飾器是一種特殊類型的聲明,它能夠被附加到類聲明,方法, 訪問符,屬性或參數上。 裝飾器使用 @e...
    涅槃快樂是金閱讀 30,471評論 3 17
  • 裝飾器是一種特殊類型的聲明,它能夠被附加到類聲明,方法, 訪問符,屬性或參數上,可以修改類的行為。 裝飾器使用 @...
    lwz4070閱讀 738評論 0 0
  • 訪問器裝飾器 訪問器裝飾器聲明在一個訪問器的聲明之前(緊靠著訪問器聲明)。 訪問器裝飾器應用于訪問器的 屬性描述符...
    2o壹9閱讀 1,052評論 1 49
  • 裝飾器求值 類中不同聲明上的裝飾器將按以下規定的順序應用: 參數裝飾器,然后依次是方法裝飾器,訪問符裝飾器,或屬性...
    2o壹9閱讀 859評論 1 49
  • 這是16年5月份編輯的一份比較雜亂適合自己觀看的學習記錄文檔,今天18年5月份再次想寫文章,發現簡書還為我保存起的...
    Jenaral閱讀 2,831評論 2 9