TypeScript 的裝飾器有哪些?
大家好,我是前端西瓜哥。
JS 的裝飾器還在提案中(提案了好久),還沒進(jìn)入正式標(biāo)準(zhǔn),掌握半成品實(shí)在性價(jià)比不高。
但裝飾器實(shí)在是太強(qiáng)了,TypeScript 還是基于第一版實(shí)現(xiàn)了自己的裝飾器特性,并標(biāo)明為實(shí)驗(yàn)性質(zhì),讓大家能夠早早地用上。
目前也不少知名的第三方庫(比如 Nest.js)使用了 TS 的裝飾器,還是有必要學(xué)習(xí)的。
但是呢,TS 的裝飾器實(shí)現(xiàn)已經(jīng)和 ECMAScript 的裝飾器提案越走越遠(yuǎn)。
但因?yàn)?TS 裝飾器被不少知名的第三方庫使用,我們可能還是得使用和標(biāo)準(zhǔn)不同的裝飾器。
以后兩種裝飾器的實(shí)現(xiàn)就要打架了,實(shí)在是太亂了,庫作者大概要吐了。
TS 中實(shí)現(xiàn)的裝飾器有:
- 類裝飾器
- 方法裝飾器
- 訪問器裝飾器
- 屬性裝飾器
- 參數(shù)裝飾器
在使用類裝飾器前,你需要在 tsconfig.json 中啟用實(shí)驗(yàn)性的裝飾器配置:
{
"compilerOptions": {
"experimentalDecorators": true
}
}
裝飾器其實(shí)是一個(gè)函數(shù),需要在被類使用前聲明。因?yàn)?nbsp;類聲明后立刻就會執(zhí)行修飾器,所以如果沒有提前聲明,就會報(bào)錯(cuò)。
類裝飾器
類裝飾器是一個(gè)函數(shù),它可以在 class 聲明時(shí)拿到 class,然后對 class 進(jìn)行一些操作。
給一個(gè)類應(yīng)用類裝飾器的方式是:在類名的上一行加上 @<裝飾器名>。
function changeDefaultPrice(constructor: Function & {defaultPrice: number}) {
constructor.defaultPrice = 100;
}
class Watermelon {
static defaultPrice = 9;
}
Watermelon.defaultPrice
可以看到上面代碼中,Watermelon 類有個(gè)靜態(tài)屬性 defaultPrice,值為 9,表示默認(rèn)西瓜只要 9 塊錢。
太便宜了,于是我實(shí)現(xiàn)了個(gè) changeDefaultPrice 裝飾器,能夠從函數(shù)參數(shù)中拿到類,并將其修改為 100 塊。
有時(shí)候,我們希望可以修改為自定義價(jià)格。這時(shí)候我們可以使用 裝飾器工廠函數(shù)。
所謂裝飾器工廠函數(shù),就是一個(gè)返回裝飾器函數(shù)的函數(shù)。通過它,我們就能利用閉包注入變量。?
function changeDefaultPrice(price = 100) {
return function (constructor: Function & { defaultPrice: number }) {
constructor.defaultPrice = price;
};
}
(50)
class Watermelon {
static defaultPrice = 9;
}
Watermelon.defaultPrice
裝飾器工廠函數(shù)不只可以用在類裝飾器中,還可以用在其他類型的裝飾器中。
方法裝飾器
方法裝飾器可以接受的參數(shù)有:
- target:類或者類的原型對象,下面的代碼拿到的就是Watermelon.prototype。如果是給 static 標(biāo)識的方法加裝飾,拿到的就是類。
- prop:方法名,下面代碼拿到的是"say" 字符串。
- descriptor:該 prop 的描述符,我們可以直接修改這個(gè)對象,然后這個(gè)對象會被裝飾器重新應(yīng)用到這個(gè)屬性上。
function changeMethod(
target: any,
prop: string | symbol,
descriptor: PropertyDescriptor
) {
descriptor.value = function () {
console.log("我沒裂開");
};
}
class Watermelon {
say() {
console.log("我裂開了");
}
}
const wm = new Watermelon();
wm.say();
上面代碼用 changeMethod 方法裝飾器重寫了 Watermelon.prototype.say 方法。
如果 TS 編譯沒能通過,你需要再給 tsconfig.json 配置上 target,且使其大于等于 ES5 版本。默認(rèn)的 ES3 版本有些 API 都不支持。
訪問器裝飾器
訪問器裝飾器,對類的 get 或 set 方法進(jìn)行裝飾。
和方法裝飾器類似,訪問器裝飾器獲得的參數(shù)有:
- target:類或類的原型對象。
- prop:成員名。
- descriptor:成員的描述符。
function doublePrice(
target: Object,
prop: string | symbol,
descriptor: PropertyDescriptor
) {
const getter = descriptor.get!;
descriptor.get = function() {
return getter.call(this) * 2;
}
}
class Watermelon {
private _price = 9;
get price() {
return this._price;
}
}
const wm = new Watermelon();
wm.price
上面代碼將 price 的值翻倍了。
屬性裝飾器
屬性裝飾器,顧名思義用于修飾類的屬性。
屬性修飾器接受的參數(shù)有:
- taget:類或類的原型對象。
- prop:屬性名。
function propDeco(target: any, prop: string | symbol) {
console.log({ target, prop });
}
class Watermelon {
goodName = "西瓜";
}
參數(shù)裝飾器
參數(shù)裝飾器用于裝飾函數(shù)參數(shù),用于類構(gòu)造器和方法。
參數(shù)裝飾器能獲得的參數(shù)有:
- target:類或類的原型對象。
- prop:函數(shù)名,或 undefined(函數(shù)為構(gòu)造函數(shù)時(shí))。
- paramIdx:被裝飾的參數(shù)的位置。
function validatePhone(target: Object, prop: string | symbol, paramIdx: number) {
}
class Watermelon {
constructor(a: string, b: string) {
console.log(a, b);
}
}
參數(shù)裝飾器的一個(gè)用途是可以做參數(shù)校驗(yàn),將某個(gè)索引的位置保存起來,然后再利用方法裝飾器拿到描述符 descriptor,對原方法做一層校驗(yàn)的封裝。
總結(jié)
總的看來,TS 裝飾器可以在類上加一些標(biāo)記,然后對應(yīng)的裝飾器就能拿到必要的信息(類的原型、方法名、描述符等),然后就可以做一些代理、記錄信息的功能增強(qiáng)。
TS 裝飾器最后編譯成的 JS 會丟掉這些裝飾器標(biāo)記,本質(zhì)其實(shí)是語法糖。
我是前端西瓜哥,歡迎關(guān)注我,學(xué)習(xí)更多前端知識。