日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問(wèn)題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營(yíng)銷解決方案
TypeScript裝飾器實(shí)用指南!

一、裝飾器的概念 Summer IS HERE

在 TypeScript 中,裝飾器就是可以添加到類及其成員的函數(shù)。TypeScript 裝飾器可以注釋和修改類聲明、方法、屬性和訪問(wèn)器。Decorator類型定義如下:

站在用戶的角度思考問(wèn)題,與客戶深入溝通,找到宣恩網(wǎng)站設(shè)計(jì)與宣恩網(wǎng)站推廣的解決方案,憑借多年的經(jīng)驗(yàn),讓設(shè)計(jì)與互聯(lián)網(wǎng)技術(shù)結(jié)合,創(chuàng)造個(gè)性化、用戶體驗(yàn)好的作品,建站類型包括:網(wǎng)站設(shè)計(jì)、成都網(wǎng)站制作、企業(yè)官網(wǎng)、英文網(wǎng)站、手機(jī)端網(wǎng)站、網(wǎng)站推廣、域名申請(qǐng)、網(wǎng)頁(yè)空間、企業(yè)郵箱。業(yè)務(wù)覆蓋宣恩地區(qū)。

type Decorator = (target: Input, context: {
  kind: string;
  name: string | symbol;
  access: {
    get?(): unknown;
    set?(value: unknown): void;
  };
  private?: boolean;
  static?: boolean;
  addInitializer?(initializer: () => void): void;
}) => Output | void;

上面的類型定義解釋如下:

  • target:代表要裝飾的元素,其類型為 Input。
  • context 包含有關(guān)如何聲明修飾方法的元數(shù)據(jù),即:
  • kind:裝飾值的類型。正如我們將看到的,這可以是類、方法、getter、setter、字段或訪問(wèn)器。
  • name:被裝飾對(duì)象的名稱。
  • access:引用 getter 和 setter 方法來(lái)訪問(wèn)裝飾對(duì)象的對(duì)象。
  • private:被裝飾的對(duì)象是否是私有類成員。
  • static:被修飾的對(duì)象是否是靜態(tài)類成員。
  • addInitializer:一種在構(gòu)造函數(shù)開頭(或定義類時(shí))添加自定義初始化邏輯的方法。
  • Output:表示 Decorator 函數(shù)返回值的類型。

二、裝飾器的類型 Summer IS HERE

接下來(lái),我們就來(lái)了解一下裝飾器的各種類型。

Summer:類裝飾器

當(dāng)將函數(shù)作為裝飾器附加到類時(shí),將收到類構(gòu)造函數(shù)作為第一個(gè)參數(shù):

type ClassDecorator = (value: Function, context: {
  kind: "class"
  name: string | undefined
  addInitializer(initializer: () => void): void
}) => Function | void

例如,假設(shè)想要使用裝飾器向 Rocket 類添加兩個(gè)屬性:fuel 和 isEmpty()。在這種情況下,可以編寫以下函數(shù):

function WithFuel(target: typeof Rocket, context): typeof Rocket {
  if (context.kind === "class") {
    return class extends target {
      fuel: number = 50
      isEmpty(): boolean {
        return this.fuel == 0
      }
    }
  }
}

在確保裝飾元素的類型確實(shí)是類之后,返回一個(gè)具有兩個(gè)附加屬性的新類。或者,可以使用原型對(duì)象來(lái)動(dòng)態(tài)添加新方法:

function WithFuel(target: typeof Rocket, context): typeof Rocket {
  if (context.kind === "class") {
    target.prototype.fuel = 50
    target.prototype.isEmpty = (): boolean => {
      return this.fuel == 0
    }
  }
}

可以按以下方式使用 WithFuel:

@WithFuel
class Rocket {}

const rocket = new Rocket()
console.log((rocket as any).fuel)
console.log(`empty? ${(rocket as any).isEmpty()}`)
/* Prints:
50
empty? false
*/

可以看到,這里將rocket轉(zhuǎn)換為any類型才能訪問(wèn)新的屬性。這是因?yàn)檠b飾器無(wú)法影響類型的結(jié)構(gòu)。

如果原始類定義了一個(gè)稍后被裝飾的屬性,裝飾器會(huì)覆蓋原始值。例如,如果Rocket有一個(gè)具有不同值的fuel屬性,WithFuel裝飾器將會(huì)覆蓋該值:

function WithFuel(target: typeof Rocket, context): typeof Rocket {
  if (context.kind === "class") {
    return class extends target {
      fuel: number = 50
      isEmpty(): boolean {
        return this.fuel == 0
      }
    }
  }
}
@WithFuel
class Rocket {
  fuel: number = 75
}

const rocket = new Rocket()
console.log((rocket as any).fuel)
// 50

Summer:方法裝飾器

方法裝飾器可以用于裝飾類方法。在這種情況下,裝飾器函數(shù)的類型如下:

type ClassMethodDecorator = (target: Function, context: {
  kind: "method"
  name: string | symbol
  access: { get(): unknown }
  static: boolean
  private: boolean
  addInitializer(initializer: () => void): void
}) => Function | void

如果希望在調(diào)用被裝飾的方法之前或之后執(zhí)行某些操作時(shí),就可以使用方法裝飾器。

例如,在開發(fā)過(guò)程中,記錄對(duì)特定方法的調(diào)用或在調(diào)用之前/之后驗(yàn)證前置/后置條件可能非常有用。此外,我們還可以影響方法的調(diào)用方式,例如通過(guò)延遲其執(zhí)行或限制在一定時(shí)間內(nèi)的調(diào)用次數(shù)。

最后,可以使用方法裝飾器將一個(gè)方法標(biāo)記為已廢棄,并記錄一條消息來(lái)警告用戶,并告知他們應(yīng)該使用哪個(gè)方法代替:

function deprecatedMethod(target: Function, context) {
  if (context.kind === "method") {
    return function (...args: any[]) {
      console.log(`${context.name} is deprecated and will be removed in a future version.`)
      return target.apply(this, args)
    }
  }
}

在這種情況下,deprecatedMethod函數(shù)的第一個(gè)參數(shù)是要裝飾的方法。確認(rèn)它確實(shí)是一個(gè)方法后(context.kind === "method"),返回一個(gè)新的函數(shù),該函數(shù)在調(diào)用實(shí)際方法之前包裝被裝飾的方法并記錄一條警告消息。

接下來(lái),可以按照以下方式使用裝飾器:

@WithFuel
class Rocket {
  fuel: number = 75
  @deprecatedMethod
  isReadyForLaunch(): Boolean {
    return !(this as any).isEmpty()
  }
}

const rocket = new Rocket()
console.log(`Is ready for launch? ${rocket.isReadyForLaunch()}`)

在isReadyForLaunch()方法中,引用了通過(guò)WithFuel裝飾器添加的isEmpty方法。注意,必須將其轉(zhuǎn)換為any類型的實(shí)例,與之前一樣。當(dāng)調(diào)用isReadyForLaunch()方法時(shí),會(huì)看到以下輸出,顯示警告消息被正確地打印出來(lái):

isReadyForLaunch is deprecated and will be removed in a future version.
Is the ready for launch? true

Summer:屬性裝飾器

屬性裝飾器與方法裝飾器的類型非常相似:

type ClassPropertyDecorator = (target: undefined, context: {
  kind: "field"
  name: string | symbol
  access: { get(): unknown, set(value: unknown): void }
  static: boolean
  private: boolean
}) => (initialValue: unknown) => unknown | void

屬性裝飾器的用例與方法裝飾器的用法也非常相似。例如,可以跟蹤對(duì)屬性的訪問(wèn)或?qū)⑵錁?biāo)記為已棄用:

function deprecatedProperty(_: any, context) {
  if (context.kind === "field") {
    return function (initialValue: any) {
      console.log(`${context.name} is deprecated and will be removed in a future version.`)
      return initialValue
    }
  }
}

代碼與為方法定義的 deprecatedMethod 裝飾器非常相似,它的用法也是如此。

Summer:訪問(wèn)器裝飾器

與方法裝飾器非常相似的是訪問(wèn)器裝飾器,它是針對(duì) getter 和 setter 的裝飾器:

type ClassSetterDecorator = (target: Function, context: {
  kind: "setter"
  name: string | symbol
  access: { set(value: unknown): void }
  static: boolean
  private: boolean
  addInitializer(initializer: () => void): void
}) => Function | void

type ClassGetterDecorator = (value: Function, context: {
  kind: "getter"
  name: string | symbol
  access: { get(): unknown }
  static: boolean
  private: boolean
  addInitializer(initializer: () => void): void
}) => Function | void

訪問(wèn)器裝飾器的定義與方法裝飾器的定義類似。例如,可以將 deprecatedMethod 和 deprecatedProperty 修飾合并到一個(gè)已棄用的函數(shù)中,該函數(shù)也支持 getter 和 setter:

function deprecated(target, context) {
  const kind = context.kind
  const msg = `${context.name} is deprecated and will be removed in a future version.`
  if (kind === "method" || kind === "getter" || kind === "setter") {
    return function (...args: any[]) {
      console.log(msg)
      return target.apply(this, args)
    }
  } else if (kind === "field") {
    return function (initialValue: any) {
      console.log(msg)
      return initialValue
    }
  }
}

三、裝飾器的用例 Summer IS HERE

上面介紹了裝飾器是什么以及如何正確使用它們,下面來(lái)看看裝飾器可以幫助我們解決的一些具體問(wèn)題。

Summer:計(jì)算執(zhí)行時(shí)間

假設(shè)想要估計(jì)運(yùn)行一個(gè)函數(shù)需要多長(zhǎng)時(shí)間,以此來(lái)衡量應(yīng)用的性能??梢詣?chuàng)建一個(gè)裝飾器來(lái)計(jì)算方法的執(zhí)行時(shí)間并將其打印在控制臺(tái)上:

class Rocket {
  @measure
  launch() {
    console.log("3... 2... 1... ");
  }
}

Rocket 類內(nèi)部有一個(gè) launch方法。要測(cè)量launch方法的執(zhí)行時(shí)間,可以附加measure 裝飾器:

import { performance } from "perf_hooks";

function measure(target: Function, context) {
  if (context.kind === "method") {
    return function (...args: any[]) {
      const start = performance.now()  
      const result = target.apply(this, args)
      const end = performance.now()

      console.log(`Time: ${end - start} s`)
      return result
    }
  }
}

可以看到,measure裝飾器會(huì)替換原始方法,并使用新方法來(lái)計(jì)算原始方法的執(zhí)行時(shí)間并將其打印到控制臺(tái)。為了計(jì)算執(zhí)行時(shí)間,可以使用 Node.js 標(biāo)準(zhǔn)庫(kù)中的性能鉤子(Performance Hooks)API。實(shí)例化一個(gè)新的Rocket對(duì)象并調(diào)用launch方法:

const rocket = new Rocket()
rocket.launch()

將得到以下結(jié)果:

3... 2... 1... 
Time: 1.062355000525713 s

Summer:使用裝飾器工廠函數(shù)

要將裝飾器配置為在特定場(chǎng)景中采取不同的行為,可以使用裝飾器工廠。裝飾器工廠是返回裝飾器的函數(shù)。這樣就能夠通過(guò)在工廠中傳遞一些參數(shù)來(lái)自定義裝飾器的行為。

來(lái)看下面的例子:

function fill(value: number) {
  return function(_, context) {
    if (context.kind === "field") {
      return function (initialValue: number) {
        return value + initialValue
      }
    }
  }
}

fill 函數(shù)返回一個(gè)裝飾器,根據(jù)從工廠傳入的值來(lái)改變屬性的值:

class Rocket {
  @fill(20)
  fuel: number = 50
}
const rocket = new Rocket()
console.log(rocket.fuel) // 70

Summer:自動(dòng)錯(cuò)誤攔截

裝飾器的另一個(gè)常見用例是檢查方法調(diào)用的前置條件和后置條件。例如,假設(shè)要在調(diào)用 launch() 方法之前確保 Fuel 至少為給定值:

class Rocket {
  fuel = 50

  launch() {
    console.log("3... 2... 1... ")
  }
}

假設(shè)有一個(gè) Rocket 類,它有一個(gè) launchToMars 方法。要發(fā)射火箭,燃料(fuel)必須高于一個(gè)值,例如 75。

下面來(lái)為它創(chuàng)建裝飾器:

function minimumFuel(fuel: number) {
  return function(target: Function, context) {
    if (context.kind === "method") {
        return function (...args: any[]) {
          if (this.fuel > fuel) {
            return target.apply(this, args)
          } else {
            console.log(`Not enough fuel. Required: ${fuel}, got ${this.fuel}`)
          }
        }
    }
  }
}

minimumFuel是一個(gè)工廠裝飾器。它接受一個(gè) fuel 參數(shù),表示啟動(dòng)特定火箭所需的燃料量。為了檢查燃料條件,將原始方法包裹在一個(gè)新方法中。注意,在運(yùn)行時(shí)可以自由地引用 this.fuel。

現(xiàn)在就可以將裝飾器應(yīng)用到launch方法上,并設(shè)置最低燃料量:

class Rocket {
  fuel = 50

  @minimumFuel(75)
  launch() {
    console.log("3... 2... 1... ")
  }
}

如果現(xiàn)在調(diào)用 launch 方法,它不會(huì)發(fā)射火箭,因?yàn)楫?dāng)前的燃料量為 50:

const rocket = new Rocket()
rocket.launch()

Not enough fuel. Required: 75, got 50

[1]裝飾器提案: https://github.com/tc39/proposal-decorators。


當(dāng)前題目:TypeScript裝飾器實(shí)用指南!
分享鏈接:http://www.5511xx.com/article/cdjeojo.html