日本综合一区二区|亚洲中文天堂综合|日韩欧美自拍一区|男女精品天堂一区|欧美自拍第6页亚洲成人精品一区|亚洲黄色天堂一区二区成人|超碰91偷拍第一页|日韩av夜夜嗨中文字幕|久久蜜综合视频官网|精美人妻一区二区三区

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

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營(yíng)銷解決方案
如何在TypeScript中使用裝飾器

介紹

TypeScript 是 JavaScript 語(yǔ)言的擴(kuò)展,它使用 JavaScript 的運(yùn)行時(shí)和編譯時(shí)類型檢查器。

創(chuàng)新互聯(lián)是專業(yè)的仙游網(wǎng)站建設(shè)公司,仙游接單;提供成都做網(wǎng)站、成都網(wǎng)站設(shè)計(jì),網(wǎng)頁(yè)設(shè)計(jì),網(wǎng)站設(shè)計(jì),建網(wǎng)站,PHP網(wǎng)站建設(shè)等專業(yè)做網(wǎng)站服務(wù);采用PHP框架,可快速的進(jìn)行仙游網(wǎng)站開(kāi)發(fā)網(wǎng)頁(yè)制作和功能擴(kuò)展;專業(yè)做搜索引擎喜愛(ài)的網(wǎng)站,專業(yè)的做網(wǎng)站團(tuán)隊(duì),希望更多企業(yè)前來(lái)合作!

這種組合允許開(kāi)發(fā)人員使用完整的 JavaScript 生態(tài)系統(tǒng)和語(yǔ)言功能,同時(shí),還可以在其之上添加可選的靜態(tài)類型檢查、枚舉、類和接口。這些額外功能之一是裝飾器的支持。

裝飾器是一種裝飾類成員或類本身的方法,具有額外的功能。

當(dāng)我們將裝飾器應(yīng)用于類或類成員時(shí),我們實(shí)際上是在調(diào)用一個(gè)函數(shù),該函數(shù)將接收被裝飾內(nèi)容的詳細(xì)信息,然后,裝飾器實(shí)現(xiàn)將能夠動(dòng)態(tài)轉(zhuǎn)換代碼,添加額外的功能,并且 減少樣板代碼。

它們是在 TypeScript 中進(jìn)行元編程的一種方式,TypeScript 是一種編程技術(shù),使程序員能夠創(chuàng)建使用來(lái)自應(yīng)用程序本身的其他代碼作為數(shù)據(jù)的代碼。

本教程將分享如何在 TypeScript 中為類和類成員創(chuàng)建自己的裝飾器,以及如何使用它們。

它將引導(dǎo)我們完成不同的代碼示例,我們可以在自己的 TypeScript 環(huán)境或 TypeScript Playground(一個(gè)允許我們直接在瀏覽器中編寫(xiě) TypeScript 的在線環(huán)境)中遵循這些示例。

準(zhǔn)備工作

要完成本教程實(shí)例,我們需要做如下準(zhǔn)備:

  • 一個(gè)環(huán)境,我們可以在其中執(zhí)行 TypeScript 程序以跟隨示例。要在本地計(jì)算機(jī)上進(jìn)行設(shè)置,您將需要以下內(nèi)容。
  • 為了運(yùn)行處理 TypeScript 相關(guān)包的開(kāi)發(fā)環(huán)境,同時(shí),安裝了 Node 和 npm(或 yarn)。本教程使用 Node.js 版本 14.3.0 和 npm 版本 6.14.5 進(jìn)行了測(cè)試。
  • 如果是要在 macOS 或 Ubuntu 18.04 上安裝,請(qǐng)按照如何在 macOS 上安裝 Node.js 和創(chuàng)建本地開(kāi)發(fā)環(huán)境或如何在 Ubuntu 18.04 上安裝 Node.js 的使用 PPA 安裝部分中的步驟進(jìn)行操作。
  • 如果您使用的是適用于 Linux 的 Windows 子系統(tǒng) (WSL),這也適用。
  •  此外,我們還需要在機(jī)器上安裝 TypeScript 編譯器 (tsc)。為此,請(qǐng)參閱官方 TypeScript 網(wǎng)站。
  • 如果我們不想在本地機(jī)器上創(chuàng)建 TypeScript 環(huán)境,我們可以使用官方的 TypeScript Playground 來(lái)跟隨。
  • 我們將需要足夠的 JavaScript 知識(shí),尤其是 ES6+ 語(yǔ)法,例如解構(gòu)、rest 運(yùn)算符和導(dǎo)入/導(dǎo)出。
  • 本教程將參考支持 TypeScript 并顯示內(nèi)聯(lián)錯(cuò)誤的文本編輯器的各個(gè)方面。這不是使用 TypeScript 所必需的,但確實(shí)可以更多地利用 TypeScript 功能。
  • 為了獲得這些好處,我們可以使用像 Visual Studio Code 這樣的文本編輯器,它完全支持開(kāi)箱即用的 TypeScript。你也可以在 TypeScript Playground 中嘗試這些好處。
  • 本教程中顯示的所有示例都是使用 TypeScript 4.2.2 版創(chuàng)建的。

在 TypeScript 中啟用裝飾器支持

目前,裝飾器在 TypeScript 中仍然是一個(gè)實(shí)驗(yàn)性功能,因此,必須先啟用它。在本節(jié)中,我們將了解如何在 TypeScript 中啟用裝飾器,具體取決于您使用 TypeScript 的方式。

TypeScript 編譯器 CLI

要在使用 TypeScript Compiler CLI (tsc) 時(shí)啟用裝飾器支持,唯一需要的額外步驟是傳遞一個(gè)附加標(biāo)志 --experimentalDecorators:

tsc --experimentalDecorators



tsconfig.json

在具有 tsconfig.json 文件的項(xiàng)目中工作時(shí),要啟用實(shí)驗(yàn)性裝飾器,我們必須將實(shí)驗(yàn)性裝飾器屬性添加到 compilerOptions 對(duì)象:

{
"compilerOptions": {
"experimentalDecorators": true
}}

在 TypeScript Playground 中,裝飾器默認(rèn)啟用。

使用裝飾器語(yǔ)法

在本節(jié)中,我們將在 TypeScript 類中應(yīng)用裝飾器。

在 TypeScript 中,我們可以使用特殊語(yǔ)法 @expression 創(chuàng)建裝飾器,其中 expression 是一個(gè)函數(shù),將在運(yùn)行時(shí)自動(dòng)調(diào)用,其中包含有關(guān)裝飾器目標(biāo)的詳細(xì)信息。

裝飾器的目標(biāo)取決于我們添加它們的位置。目前,裝飾器可以添加到類的以下組件中:

  • 類聲明本身
  • 特性
  • 訪問(wèn)器
  • 方法
  • 參數(shù)

例如,假設(shè)我們有一個(gè)名為 seal 的裝飾器,它在類中調(diào)用 Object.seal。要使用我們的裝飾器,我們可以編寫(xiě)以下內(nèi)容:

@sealed
class Person {}

請(qǐng)注意,在突出顯示的代碼中,我們?cè)诿芊庋b飾器的目標(biāo)之前添加了裝飾器,在本例中為 Person 類聲明。

這同樣適用于所有其他類型的裝飾器:

@classDecorator
class Person {
@propertyDecorator
public name: string;
@accessorDecorator
get fullName() {
// ...
}
@methodDecorator
printName(@parameterDecorator prefix: string) {
// ...
}
}

要添加多個(gè)裝飾器,請(qǐng)將它們一個(gè)接一個(gè)地添加在一起:

@decoratorA
@decoratorB
class Person {}

在 TypeScript 中創(chuàng)建類裝飾器

在本節(jié)中,我們將完成在 TypeScript 中創(chuàng)建類裝飾器的步驟。

對(duì)于名為 @decoratorA 的裝飾器,我們告訴 TypeScript 它應(yīng)該調(diào)用函數(shù) decoratorA。將調(diào)用 decoratorA 函數(shù),其中包含有關(guān)如何在代碼中使用裝飾器的詳細(xì)信息。

例如,如果您將裝飾器應(yīng)用于類聲明,則該函數(shù)將接收有關(guān)該類的詳細(xì)信息。此功能必須在您的裝飾器工作的范圍內(nèi)。

要?jiǎng)?chuàng)建自己的裝飾器,我們必須創(chuàng)建一個(gè)與裝飾器同名的函數(shù)。也就是說(shuō),要?jiǎng)?chuàng)建您在上一節(jié)中看到的密封類裝飾器,您必須創(chuàng)建一個(gè)接收一組特定參數(shù)的密封函數(shù)。讓我們這樣做:

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

傳遞給裝飾器的參數(shù)將取決于裝飾器的使用位置。第一個(gè)參數(shù)通常稱為目標(biāo)。

密封裝飾器將僅用于類聲明,因此,我們的函數(shù)將接收一個(gè)參數(shù),即目標(biāo),其類型為 Function。這將是應(yīng)用裝飾器的類的構(gòu)造函數(shù)。

然后,在密封函數(shù)中,在目標(biāo)(即類構(gòu)造函數(shù))以及它們的原型上調(diào)用 Object.seal。當(dāng)這樣做時(shí),不能將新屬性添加到類構(gòu)造函數(shù)或其屬性中,并且現(xiàn)有屬性將被標(biāo)記為不可配置。

重要的是要記住,目前在使用裝飾器時(shí)無(wú)法擴(kuò)展目標(biāo)的 TypeScript 類型。這意味著,例如,你無(wú)法使用裝飾器將新字段添加到類并使其成為類型安全的。

如果在密封類裝飾器中返回了一個(gè)值,該值將成為該類的新構(gòu)造函數(shù)。如果想完全覆蓋類構(gòu)造函數(shù),這很有用。

已經(jīng)創(chuàng)建了第一個(gè)裝飾器,并將它與一個(gè)類一起使用。

接下來(lái),我們將學(xué)習(xí)如何創(chuàng)建裝飾器工廠。

創(chuàng)建裝飾器工廠

有時(shí),我們需要在應(yīng)用裝飾器時(shí)將其他選項(xiàng)傳遞給裝飾器,為此,我們必須使用裝飾器工廠。

在這里,我們將學(xué)習(xí)如何創(chuàng)建和使用這些工廠。

裝飾器工廠是返回另一個(gè)函數(shù)的函數(shù)。他們收到這個(gè)名字是因?yàn)樗麄儾皇茄b飾器實(shí)現(xiàn)本身。

相反,它們返回另一個(gè)負(fù)責(zé)實(shí)現(xiàn)裝飾器的函數(shù)并充當(dāng)包裝函數(shù)。通過(guò)允許客戶端代碼在使用裝飾器時(shí)將選項(xiàng)傳遞給裝飾器,它們?cè)谑寡b飾器可定制方面很有用。

假設(shè),有一個(gè)名為 decoratorA 的類裝飾器,并且,我們想添加一個(gè)可以在調(diào)用裝飾器時(shí)設(shè)置的選項(xiàng),例如,布爾標(biāo)志,可以通過(guò)編寫(xiě)類似于以下的裝飾器工廠來(lái)實(shí)現(xiàn)此目的:

const decoratorA = (someBooleanFlag: boolean) => {
return (target: Function) => {
}
}

在這里,decoratorA 函數(shù)返回另一個(gè)帶有裝飾器實(shí)現(xiàn)的函數(shù)。注意,裝飾器工廠如何接收一個(gè)布爾標(biāo)志作為它的唯一參數(shù):

const decoratorA = (someBooleanFlag: boolean) => {
return (target: Function) => {
}
}

我們可以在使用裝飾器時(shí)傳遞此參數(shù)的值。

請(qǐng)參閱以下示例中突出顯示的代碼:

const decoratorA = (someBooleanFlag: boolean) => {
return (target: Function) => {
}
}
@decoratorA(true)
class Person {}

在這里,當(dāng)我們使用 decoratorA 裝飾器時(shí),將調(diào)用裝飾器工廠,并將 someBooleanFlag 參數(shù)設(shè)置為 true。

然后,裝飾器實(shí)現(xiàn)本身將運(yùn)行。這允許我們根據(jù)使用方式更改裝飾器的行為,從而,使我們的裝飾器易于自定義和通過(guò)應(yīng)用程序重用。

請(qǐng)注意,我們需要傳遞裝飾器工廠預(yù)期的所有參數(shù)。如果,我們只是應(yīng)用裝飾器而不傳遞任何參數(shù),如下例所示:

const decoratorA = (someBooleanFlag: boolean) => {
return (target: Function) => {
}
}
@decoratorA
class Person {}

TypeScript 編譯器會(huì)給你兩個(gè)錯(cuò)誤,這可能會(huì)因裝飾器的類型而異。對(duì)于類裝飾器,錯(cuò)誤是 1238 和 1240:

Unable to resolve signature of class decorator when called as an expression.
Type '(target: Function) => void' is not assignable to type 'typeof Person'.
Type '(target: Function) => void' provides no match for the signature 'new (): Person'. (1238)
Argument of type 'typeof Person' is not assignable to parameter of type 'boolean'. (2345)

我們剛剛創(chuàng)建了一個(gè)能夠接收參數(shù)并根據(jù)這些參數(shù)更改其行為的裝飾器工廠。

在下一步中,我們將學(xué)習(xí)如何創(chuàng)建屬性裝飾器。

創(chuàng)建屬性裝飾器

類屬性是另一個(gè)可以使用裝飾器的地方,在這里,我們將了解如何創(chuàng)建它們。

任何屬性裝飾器都接收以下參數(shù):

  • 對(duì)于靜態(tài)屬性,類的構(gòu)造函數(shù),對(duì)于所有其他屬性,類的原型。
  • 成員的姓名。

目前,沒(méi)有辦法獲取屬性描述符作為參數(shù)。這是由于 TypeScript 中屬性裝飾器的初始化方式。

這是一個(gè)裝飾器函數(shù),它將成員的名稱打印到控制臺(tái):

const printMemberName = (target: any, memberName: string) => {
console.log(memberName);
};
class Person {
@printMemberName
name: string = "Jon";
}

當(dāng)我們運(yùn)行上面的 TypeScript 代碼時(shí),你會(huì)在控制臺(tái)中看到如下打?。?

name

我們可以使用屬性裝飾器來(lái)覆蓋被裝飾的屬性。這可以通過(guò)Object.defineProperty與屬性的新 setter 和 getter 一起使用來(lái)完成。

讓我們看看如何創(chuàng)建一個(gè)名為 的裝飾器allowlist,它只允許將屬性設(shè)置為靜態(tài)允許列表中存在的值:

const allowlist = ["Jon", "Jane"];
const allowlistOnly = (target: any, memberName: string) => {
let currentValue: any = target[memberName];
Object.defineProperty(target, memberName, {
set: (newValue: any) => {
if (!allowlist.includes(newValue)) {
return;
}
currentValue = newValue;
},
get: () => currentValue
});
};

首先,我們要在代碼頂部創(chuàng)建一個(gè)靜態(tài)許可名單:

const allowlist = ["Jon", "Jane"];

然后,我們創(chuàng)建一個(gè)屬性裝飾器:

const allowlistOnly = (target: any, memberName: string) => {
let currentValue: any = target[memberName];
Object.defineProperty(target, memberName, {
set: (newValue: any) => {
if (!allowlist.includes(newValue)) {
return;
}
currentValue = newValue;
},
get: () => currentValue
});
};

請(qǐng)注意,我們?nèi)绾问褂?any 作為目標(biāo)的類型:

const allowlistOnly = (target: any, memberName: string) => {

對(duì)于屬性裝飾器來(lái)說(shuō),目標(biāo)參數(shù)的類型可以是類的構(gòu)造函數(shù),也可以是類的原型,在這種情況下使用any比較容易。

在裝飾器實(shí)現(xiàn)的第一行中,我們將被裝飾的屬性的當(dāng)前值存儲(chǔ)到 currentValue 變量中:

let currentValue: any = target[memberName];

對(duì)于靜態(tài)屬性,這將設(shè)置為其默認(rèn)值(如果有)。

對(duì)于非靜態(tài)屬性,這將始終未定義。這是因?yàn)樵谶\(yùn)行時(shí),在編譯的 JavaScript 代碼中,裝飾器在實(shí)例屬性設(shè)置為其默認(rèn)值之前運(yùn)行。

然后,我們將使用 Object.defineProperty 覆蓋該屬性:

Object.defineProperty(target, memberName, {
set: (newValue: any) => {
if (!allowlist.includes(newValue)) {
return;
}
currentValue = newValue;
},
get: () => currentValue
});

Object.defineProperty 調(diào)用有一個(gè) getter 和一個(gè) setter。getter 返回存儲(chǔ)在 currentValue 變量中的值。

如果 currentVariable 在允許列表中,setter 會(huì)將其值設(shè)置為 newValue。

讓我們使用您剛剛編寫(xiě)的裝飾器。創(chuàng)建以下 Person 類:

class Person {
@allowlistOnly
name: string = "Jon";
}



我們現(xiàn)在將創(chuàng)建類的新實(shí)例,并測(cè)試設(shè)置并獲取name實(shí)例屬性:

const allowlist = ["Jon", "Jane"];
const allowlistOnly = (target: any, memberName: string) => {
let currentValue: any = target[memberName];
Object.defineProperty(target, memberName, {
set: (newValue: any) => {
if (!allowlist.includes(newValue)) {
return;
}
currentValue = newValue;
},
get: () => currentValue
});
};
class Person {
@allowlistOnly
name: string = "Jon";
}
const person = new Person();
console.log(person.name);
person.name = "Peter";
console.log(person.name);
person.name = "Jane";
console.log(person.name);

運(yùn)行代碼,我們應(yīng)該看到以下輸出:

OutputJon
Jon
Jane

該值永遠(yuǎn)不會(huì)設(shè)置為 Peter,因?yàn)?Peter 不在允許列表中。

如果我們想讓代碼更具可重用性,允許在應(yīng)用裝飾器時(shí)設(shè)置允許列表,該怎么辦?這是裝飾器工廠的一個(gè)很好的用例。

讓我們通過(guò) allowlistOnly 裝飾器變成裝飾器工廠來(lái)做到這一點(diǎn)。

const allowlistOnly = (allowlist: string[]) => {
return (target: any, memberName: string) => {
let currentValue: any = target[memberName];
Object.defineProperty(target, memberName, {
set: (newValue: any) => {
if (!allowlist.includes(newValue)) {
return;
}
currentValue = newValue;
},
get: () => currentValue
});
};
}

在這里,我們將之前的實(shí)現(xiàn)包裝到另一個(gè)函數(shù)中,即裝飾器工廠。裝飾器工廠接收一個(gè)名為允許列表的參數(shù),它是一個(gè)字符串?dāng)?shù)組。

現(xiàn)在,要使用的裝飾器,我們必須通過(guò)許可名單,如以下突出顯示的代碼所示:

class Person {
@allowlistOnly(["Claire", "Oliver"])
name: string = "Claire";
}

嘗試運(yùn)行與之前編寫(xiě)的代碼類似的代碼,但有新的更改:

const allowlistOnly = (allowlist: string[]) => {
return (target: any, memberName: string) => {
let currentValue: any = target[memberName];
Object.defineProperty(target, memberName, {
set: (newValue: any) => {
if (!allowlist.includes(newValue)) {
return;
}
currentValue = newValue;
},
get: () => currentValue
});
};
}
class Person {
@allowlistOnly(["Claire", "Oliver"])
name: string = "Claire";
}
const person = new Person();
console.log(person.name);
person.name = "Peter";
console.log(person.name);
person.name = "Oliver";
console.log(person.name);



輸出如下:

OutputClaire
Claire
Oliver

顯示它按預(yù)期工作,person.name 永遠(yuǎn)不會(huì)設(shè)置為 Peter,因?yàn)?Peter 不在給定的白名單中。

現(xiàn)在,我們已經(jīng)使用普通裝飾器函數(shù)和裝飾器工廠創(chuàng)建了第一個(gè)屬性裝飾器,是時(shí)候看看如何為類訪問(wèn)器創(chuàng)建裝飾器了。

創(chuàng)建訪問(wèn)器裝飾器

在這里,我們將了解裝飾類訪問(wèn)器。

就像屬性裝飾器一樣,訪問(wèn)器中使用的裝飾器接收以下參數(shù):

  • 對(duì)于靜態(tài)屬性,類的構(gòu)造函數(shù),對(duì)于所有其他屬性,類的原型。
  • 成員的姓名。

但與屬性裝飾器不同的是,它還接收第三個(gè)參數(shù),即訪問(wèn)器成員的屬性描述符。

鑒于 Property Descriptors 包含特定成員的 setter 和 getter,訪問(wèn)器裝飾器只能應(yīng)用于單個(gè)成員的 setter 或 getter,而不能同時(shí)應(yīng)用于兩者。

如果我們從訪問(wèn)器裝飾器返回一個(gè)值,該值將成為 getter 和 setter 成員的訪問(wèn)器的新屬性描述符。

下面是一個(gè)可用于更改 getter/setter 訪問(wèn)器的可枚舉標(biāo)志的裝飾器示例:

const enumerable = (value: boolean) => {
return (target: any, memberName: string, propertyDescriptor: PropertyDescriptor) => {
propertyDescriptor.enumerable = value;
}
}

請(qǐng)注意示例中,我們是如何使用裝飾器工廠的。這允許我們?cè)谡{(diào)用裝飾器時(shí)指定可枚舉標(biāo)志。

以下是如何使用裝飾器:

class Person {
firstName: string = "Jon"
lastName: string = "Doe"
@enumerable(true)
get fullName () {
return `${this.firstName} ${this.lastName}`;
}
}

訪問(wèn)器裝飾器類似于屬性裝飾器。唯一的區(qū)別是它們接收帶有屬性描述符的第三個(gè)參數(shù)?,F(xiàn)在,我們已經(jīng)創(chuàng)建了第一個(gè)訪問(wèn)器裝飾器。

接下來(lái),我們將學(xué)習(xí)如何創(chuàng)建方法裝飾器。

創(chuàng)建方法裝飾器

在這里,我們將學(xué)習(xí)如何使用方法裝飾器。

方法裝飾器的實(shí)現(xiàn)與創(chuàng)建訪問(wèn)器裝飾器的方式非常相似。傳遞給裝飾器實(shí)現(xiàn)的參數(shù)與傳遞給訪問(wèn)器裝飾器的參數(shù)相同。

讓我們重用之前創(chuàng)建的同一個(gè)可枚舉裝飾器,但這次是在以下 Person 類的 getFullName 方法中:

const enumerable = (value: boolean) => {
return (target: any, memberName: string, propertyDescriptor: PropertyDescriptor) => {
propertyDescriptor.enumerable = value;
}
}
class Person {
firstName: string = "Jon"
lastName: string = "Doe"
@enumerable(true)
getFullName () {
return `${this.firstName} ${this.lastName}`;
}
}

如果我們從方法裝飾器返回一個(gè)值,該值將成為該方法的新屬性描述符。

讓我們創(chuàng)建一個(gè)deprecated的裝飾器,它在使用該方法時(shí)將傳遞的消息打印到控制臺(tái),記錄一條消息說(shuō)該方法已被棄用:

const deprecated = (deprecationReason: string) => {
return (target: any, memberName: string, propertyDescriptor: PropertyDescriptor) => {
return {
get() {
const wrapperFn = (...args: any[]) => {
console.warn(`Method ${memberName} is deprecated with reason: ${deprecationReason}`);
propertyDescriptor.value.apply(this, args)
}
Object.defineProperty(this, memberName, {
value: wrapperFn,
configurable: true,
writable: true
});
return wrapperFn;
}
}
}
}



在這里,我們正在使用裝飾器工廠創(chuàng)建裝飾器。這個(gè)裝飾器工廠接收一個(gè)字符串類型的參數(shù),這是棄用的原因,如下面突出顯示的部分所示:

const deprecated = (deprecationReason: string) => {
return (target: any, memberName: string, propertyDescriptor: PropertyDescriptor) => {
// ...
}
}



deprecationReason 將在稍后將棄用消息記錄到控制臺(tái)時(shí)使用。在不推薦使用裝飾器的實(shí)現(xiàn)中,我們正在返回一個(gè)值。當(dāng)我們從方法裝飾器返回值時(shí),該值將覆蓋該成員的屬性描述符。

我們正在利用這一點(diǎn)為裝飾類方法添加一個(gè)吸氣劑。這樣,我們就可以更改方法本身的實(shí)現(xiàn)。

但是為什么不直接使用 Object.defineProperty 而不是為方法返回一個(gè)新的屬性裝飾器呢?這是必要的,因?yàn)?,我們需要訪問(wèn) this 的值,對(duì)于非靜態(tài)類方法,它綁定到類實(shí)例。

如果,我們直接使用 Object.defineProperty ,將無(wú)法檢索 this 的值,并且如果該方法以任何方式使用 this ,則當(dāng)從裝飾器實(shí)現(xiàn)中運(yùn)行包裝的方法時(shí),裝飾器會(huì)破壞我們的代碼。

在這樣情況下,getter 本身的 this 值綁定到非靜態(tài)方法的類實(shí)例,并綁定到靜態(tài)方法的類構(gòu)造函數(shù)。

然后,在你的 getter 中創(chuàng)建一個(gè)本地包裝函數(shù),稱為 wrapperFn,此函數(shù)使用 console.warn 將消息記錄到控制臺(tái),傳遞從裝飾器工廠收到的 deprecationReason,然后使用 propertyDescriptor.value 調(diào)用原始方法。

apply(this, args),以這種方式調(diào)用原始方法,并將其 this 值正確綁定到類實(shí)例,以防它是非靜態(tài)方法。

然后,我們將使用 defineProperty 覆蓋類中方法的值。這就像一種記憶機(jī)制,因?yàn)閷?duì)同一方法的多次調(diào)用將不再調(diào)用 getter,而是直接調(diào)用 wrapperFn。

我們現(xiàn)在正在使用 Object.defineProperty 將類中的成員設(shè)置為將wrapperFn 作為其值。

讓我們使用已棄用的裝飾器:

const deprecated = (deprecationReason: string) => {
return (target: any, memberName: string, propertyDescriptor: PropertyDescriptor) => {
return {
get() {
const wrapperFn = (...args: any[]) => {
console.warn(`Method ${memberName} is deprecated with reason: ${deprecationReason}`);
propertyDescriptor.value.apply(this, args)
}
Object.defineProperty(this, memberName, {
value: wrapperFn,
configurable: true,
writable: true
});
return wrapperFn;
}
}
}
}
class TestClass {
static staticMember = true;
instanceMember: string = "hello"
@deprecated("Use another static method")
static deprecatedMethodStatic() {
console.log('inside deprecated static method - staticMember =', this.staticMember);
}
@deprecated("Use another instance method")
deprecatedMethod () {
console.log('inside deprecated instance method - instanceMember =', this.instanceMember);
}
}
TestClass.deprecatedMethodStatic();
const instance = new TestClass();
instance.deprecatedMethod();

在這里,我們創(chuàng)建了一個(gè)具有兩個(gè)屬性的 TestClass:一個(gè)是靜態(tài)的,一個(gè)是非靜態(tài)的。我們還創(chuàng)建了兩種方法:一種是靜態(tài)的,一種是非靜態(tài)的。

然后,我們將已棄用的裝飾器應(yīng)用于這兩種方法。運(yùn)行代碼時(shí),控制臺(tái)中會(huì)出現(xiàn)以下內(nèi)容:

Output(warning) Method deprecatedMethodStatic is deprecated with reason: Use another static method
inside deprecated static method - staticMember = true
(warning)) Method deprecatedMethod is deprecated with reason: Use another instance method
inside deprecated instance method - instanceMember = hello

這表明這兩種方法都使用了包裝函數(shù)正確包裝,該函數(shù)將一條消息記錄到控制臺(tái)并說(shuō)明棄用原因。

你現(xiàn)在已經(jīng)使用 TypeScript 創(chuàng)建了你的第一個(gè)方法裝飾器。

接下來(lái),我們將學(xué)習(xí)如何創(chuàng)建 TypeScript 支持的最后一個(gè)裝飾器類型,即參數(shù)裝飾器。

創(chuàng)建參數(shù)裝飾器

參數(shù)裝飾器可以用在類方法的參數(shù)中。

在這里,我們將學(xué)習(xí)如何創(chuàng)建一個(gè)與參數(shù)一起使用的裝飾器函數(shù),

接收以下參數(shù):

  • 對(duì)于靜態(tài)屬性,類的構(gòu)造函數(shù)。對(duì)于所有其他屬性,類的原型。
  • 成員的姓名。

方法參數(shù)列表中參數(shù)的索引。

無(wú)法更改與參數(shù)本身相關(guān)的任何內(nèi)容,因此,此類裝飾器僅對(duì)觀察參數(shù)使用本身有用(除非您使用更高級(jí)的東西,例如反射元數(shù)據(jù))。

這是一個(gè)裝飾器的示例,它打印被裝飾的參數(shù)的索引以及方法名稱:

function print(target: Object, propertyKey: string, parameterIndex: number) {
console.log(`Decorating param ${parameterIndex} from ${propertyKey}`);
}

然后,你可以像這樣使用你的參數(shù)裝飾器:

class TestClass {
testMethod(param0: any, @print param1: any) {}
}

運(yùn)行上述代碼應(yīng)在控制臺(tái)中顯示以下內(nèi)容:

Decorating param 1 from testMethod

我們現(xiàn)在已經(jīng)創(chuàng)建并執(zhí)行了一個(gè)參數(shù)裝飾器,并打印出返回裝飾參數(shù)索引的結(jié)果。

總結(jié)

在本教程中,我們已經(jīng)實(shí)現(xiàn)了 TypeScript 支持的所有裝飾器,將它們與類一起使用,并了解了它們之間的區(qū)別。

現(xiàn)在可以開(kāi)始編寫(xiě)自己的裝飾器來(lái)減少代碼庫(kù)中的樣板代碼,或者更加自信地使用帶有庫(kù)(例如 Mobx)的裝飾器。

以上就是我跟你分享的全部?jī)?nèi)容,如果你覺(jué)得有用,請(qǐng)記得分享給你身邊的朋友,也許能夠幫助到他。

最后,感謝您的閱讀,祝編程愉快!


當(dāng)前名稱:如何在TypeScript中使用裝飾器
URL地址:http://www.dlmjj.cn/article/djpopce.html