ts使用裝飾器


裝飾器

裝飾器是一種特殊類型的聲明,它能夠被附加到類聲明,方法,訪問符,屬性或參數上。 裝飾器使用@expression這種形式,expression必須是一個函數,它會在運行時被調用,被裝飾的聲明信息做為參數傳入。

Typescript中的裝飾器是一項實驗性功能,需要在tsconfig.json中開啟該特性
{
    "compilerOptions": {
        "experimentalDecorators": true
    }
}

  

例如,有一個@sealed裝飾器,我們這樣定義sealed:

function sealed(target: any) {
    // 操作被裝飾對象
}

  

裝飾器工廠

如果需要給裝飾器添加一些動態行為,比如開發一個監控統計的裝飾器,需要傳入當前統計的事件名稱,有多個事件名稱時只需要變更傳入的事件名而不用重復定義裝飾器。

這時候需要使用到裝飾器工廠。裝飾器工廠也是一個函數,只不過它的返回值是一個裝飾器。例如如下的事件監控裝飾器:

function event(eventName: string) {
    return function(target: any) {
        // 獲取到當前eventName和被裝飾對象進行操作
    }
}

  

裝飾器組合

多個裝飾器可以同時應用到被裝飾對象上,例如下面的例子:

@sealed
@test('test')
class Demo {

}

  

裝飾器執行順序:

  1. 裝飾器工廠需要先求值,再裝飾,求值順序是由上到下
  2. 裝飾器可以直接求值,裝飾順序是由下到上

上面的說明可以難以理解,下面舉一個實際的例子:

function f() {
    console.log('f求值');
    return function(target: any) {
        console.log('f裝飾');
    }
}

function g() {
    console.log('g求值');
    return function(target: any) {
        console.log('g裝飾');
    }
}

@f()
@g()
class Demo {

}

  

上例的執行順序為

f求值
g求值
g裝飾
f裝飾

因為先求值,所以在上面的f會比g先求值。因為裝飾器是由下到上裝飾,所以求值后的g比f先執行。

裝飾器類型

根據被裝飾的對象不同,裝飾器分為以下幾類:

  1. 類裝飾器
  2. 方法裝飾器
  3. 屬性裝飾器
  4. 函數參數裝飾器

類裝飾器

類裝飾器在定義類的地方。類裝飾器可以監視、修改或替換類定義。類的構造函數將作為唯一參數傳遞給裝飾器。如果類裝飾器返回一個值,它會使用返回的構造函數替換原來的類聲明。

function sealed(target: Function) {
    Object.seal(target);
    Object.seal(target.prototype);
}

@sealed
class Demo {}

  

下面來一個替換構造函數的示例:

function replace<T extends {new(...args: any[]):{}}>(target: T) {
    return class extends target {
        newname = "newName";
        age = 18
    }
}

@replace
class Demo {
    oldname = "oldname";
    constructor(oldname: string) {
        this.oldname = oldname;
    }
}

console.log(new Demo("oldname"));

  

以上例程會輸出

class_1 { oldname: 'oldname', newname: 'newName', age: 18 }

可以看到通過裝飾器新增的newname和age屬性已經成功注入了。

方法裝飾器

方法裝飾器用來裝飾類的方法(靜態方法和實例方法都可以)。方法裝飾器可以監視、修改或替換方法定義。
方法裝飾器接收3個參數:

  1. 類的原型對象,如果是靜態方法則為類的構造函數
  2. 方法名稱
  3. 方法的屬性描述符

下面是一個修改方法行為的裝飾器:

function hack(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
    const oldFunction = target[propertyKey]; // 獲取方法引用
    const newFunction = function(...args: any[]) {
        console.log('call function ', propertyKey);
        oldFunction.call(target, ...args);
    }
    descriptor.value = newFunction; // 替換原聲明
}

class Demo {
    @hack
    demo() {
        console.log('call demo');
    }
}

const demo = new Demo();
demo.demo();

  

以上例程輸出如下:

call function demo call demo

屬性裝飾器

屬性裝飾器用來裝飾類的成員屬性。屬性裝飾器接收兩個參數:

  1. 類的原型對象,如果是靜態方法則為類的構造函數
  2. 屬性名
function demo(value: string) {
    return function(target: any, propertyKey: string) {
        target[propertyKey] = value;
    }
}
class Demo {
    @demo('haha') name?: string;
}

const d = new Demo();
console.log(d.name);

  

屬性裝飾器多用在屬性依賴注入上面

函數參數裝飾器

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

  1. 對於靜態成員來說是類的構造函數,對於實例成員是類的原型對象。
  2. 參數的名字。
  3. 參數在函數參數列表中的索引。
function PathParam(paramDesc: string) {
    return function (target: any, paramName: string, paramIndex: number) {
        !target.$meta && (target.$meta = {});
        target.$meta[paramIndex] = paramDesc;
    }
}

class Demo {
    constructor() { }
    getUser( @PathParam("userId") userId: string) { }
}

console.log((<any>Demo).prototype.$meta);

  

以上例程輸出

'0': 'userId' }

函數參數裝飾器可以用在開發Web框架時自動注入請求參數。

結語

裝飾器的介紹到這里就暫時結束了,裝飾器的存在讓Typescript有了與Java和C#等語言的注解相同的功能。當然,基於裝飾器能做的工作是相當多的,注明的Angular2就大量使用了裝飾器來分離業務邏輯。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM