淺談JS裝飾器以及裝飾器在TS中的使用方式
類裝飾器
什么是類裝飾器呢?
類裝飾器的本質(zhì)是一個函數(shù),該函數(shù)接受一個參數(shù),表示類本身(構(gòu)造函數(shù)本身)。 那么類裝飾器該如何使用呢?
function decorator (target) {
}
@decorator
class A {
}這樣的話就完成了類裝飾器的編寫。不難看出類裝飾器的調(diào)用方式就是@函數(shù)名的方式放在一個類的聲明之前。那如果在TS中使用的話,我們都知道TS中有類型檢查,那對于裝飾器而言,主要在形參的類型上需要我們自己定義。那類裝飾器的形參表示的是類本身,該如何定義呢?
我們知道JS中的類其實(shí)就是一個函數(shù),所以我們可以使用Function來對類進(jìn)行類型定義。但是這不是很嚴(yán)謹(jǐn),因?yàn)轭愂强梢杂?code>new關(guān)鍵字來聲明的,并且會返回一個object,所以更推薦使用new (參數(shù)) => object來定義類的類型。那么以上代碼就會被改造成這樣:
type decoratorType = new (...args:any[]) => object;
function decorator ( target : decorarorType ) {
};
@decorator;
class A {
}值得注意的是,ts可能會在類名處有錯誤提示,這是因?yàn)檠b飾器在ts中還處于試驗(yàn)階段。我們可以在 tsconfig.json中配置experimentalDecorators為true來規(guī)避這個報錯。
類裝飾器的運(yùn)行時機(jī)
類裝飾器的運(yùn)行時間是在類定義后直接運(yùn)行。我們可以驗(yàn)證一下:

運(yùn)行后發(fā)現(xiàn),在class A定義完成后立即就輸出了我是decorator。
從編譯結(jié)果上也能很容易的看出,裝飾器是在類定義后直接調(diào)用的:
// 這里有一個函數(shù) 他的第一個參數(shù)decorators表示裝飾器數(shù)組,第二個參數(shù)target表示被裝飾的類本身。
var __decorate =
(this && this.__decorate) ||
function (decorators, target, key, desc) {
var c = arguments.length,
r =
c < 3
? target
: desc === null
? (desc = Object.getOwnPropertyDescriptor(target, key))
: desc,
d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function")
// 調(diào)用裝飾器
r = Reflect.decorate(decorators, target, key, desc);
else
for (var i = decorators.length - 1; i >= 0; i--)
// 調(diào)用裝飾器(從后往前)
if ((d = decorators[i]))
r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
// 這時我們定義的裝飾器函數(shù)
function decotator(target) {
console.log("我是decorator");
}
// 定義類
let A = class A {};
// 裝飾器和類作為參數(shù)傳入__decorate中運(yùn)行。
A = __decorate([decotator], A);裝飾器的返回值
裝飾器可以有以下幾種返回值:
- void
- 一個新的類:會將新的類替換掉裝飾目標(biāo)
這里具體演示第二種返回值----返回一個新的類。在裝飾器中返回新的類class B。此時new一個class A的實(shí)例,打印后發(fā)現(xiàn)為class B的實(shí)例對象。說明class A通過裝飾器已經(jīng)被替換為class B了。

所以說在裝飾器中可以通過以上方式,增強(qiáng)被裝飾類的功能。不過在ts中這種方式可能丟失一些類型檢查。如下圖:

這是因?yàn)檠b飾器是一個通用的,雖然在當(dāng)前場景下因?yàn)?code>class B繼承了裝飾器傳入的class A,因?yàn)轭愌b飾器的入?yún)⑹?strong>動態(tài)的,所以ts并不能知道到底有沒有prop1這個屬性。當(dāng)然這樣并不會影響代碼的功能,只是在ts中會丟失類型檢查。
那么如果我想讓裝飾器能夠接受一些額外的內(nèi)容,該怎么做呢?在實(shí)際開發(fā)中,可能需要某些數(shù)據(jù)來參與邏輯。那么我們可以用以下方式來實(shí)現(xiàn):
type decoratorType = new (...args: any) => object;
function decorator(str: string) {
return function (target: decoratorType) {
console.log(str);
console.log(target);
};
}
@decorator("這是一個類")
class A {}因?yàn)樾枰邮茴~外的信息,所以這必然是需要一個函數(shù)調(diào)用的形式。這里 @decorator接受了一個字符串參數(shù)。并且decorator函數(shù)接受一個形參str且返回一個新的函數(shù),所以decorator函數(shù)返回的函數(shù)會作為真正的裝飾器,它可以接收到被修飾類class A。我們可以看一下運(yùn)行結(jié)果。

多個裝飾器的情況
那么如果有多個裝飾器呢?他們的運(yùn)行順序是我們想的那樣從上到下依次運(yùn)行的嗎?比如有以下代碼:
type decoratorType = new (...args: any) => object;
function decorator1(target: decoratorType) {
console.log("我是裝飾器1");
}
function decorator2(target: decoratorType) {
console.log("我是裝飾器2");
}
function decorator3(target: decoratorType) {
console.log("我是裝飾器3");
}
@decorator1
@decorator2
@decorator3
class A {}按照我們一貫的思維,因?yàn)槭峭酱a,所以會按順序執(zhí)行,事實(shí)真的是這樣嗎?讓我們來看一下運(yùn)行結(jié)果:

不難發(fā)現(xiàn)裝飾器的運(yùn)行順序是遵循后加入先調(diào)用的形式!從編譯結(jié)果也能發(fā)現(xiàn):
var __decorate =
(this && this.__decorate) ||
function (decorators, target, key, desc) {
var c = arguments.length,
r =
c < 3
? target
: desc === null
? (desc = Object.getOwnPropertyDescriptor(target, key))
: desc,
d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function")
// 調(diào)用裝飾器
r = Reflect.decorate(decorators, target, key, desc);
else
for (var i = decorators.length - 1; i >= 0; i--)
//看這里 調(diào)用裝飾器(從后往前因?yàn)槭莍--且i的初始值是裝飾器數(shù)組的實(shí)際長度)
if ((d = decorators[i]))
r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};那如果把裝飾器作為一個函數(shù)來調(diào)用呢?
type decoratorType = new (...args: any) => object;
function decorator1(str: string) {
console.log("第一個函數(shù)運(yùn)行了");
return function (target: decoratorType) {
console.log(str);
};
}
function decorator2(str: string) {
console.log("第二個函數(shù)運(yùn)行了");
return function (target: decoratorType) {
console.log(str);
};
}
function decorator3(str: string) {
console.log("第三個函數(shù)運(yùn)行了");
return function (target: decoratorType) {
console.log(str);
};
}
@decorator1("我是裝飾器1")
@decorator2("我是裝飾器2")
@decorator3("我是裝飾器3")
class A {}當(dāng)作為函數(shù)調(diào)用時,會先執(zhí)行函數(shù)體,因?yàn)樵摵瘮?shù)又返回了一個函數(shù),所以返回的新函數(shù)會作為裝飾器。運(yùn)行第一個函數(shù)會的到第一個裝飾器,以此類推會獲得三個裝飾器。而裝飾器是按照后加入先調(diào)用的形式,所以會輸出以下結(jié)果:

從編譯結(jié)果來看能更好的理解:
function decorator1(str) {
console.log("第一個函數(shù)運(yùn)行了");
return function (target) {
console.log(str);
};
}
function decorator2(str) {
console.log("第二個函數(shù)運(yùn)行了");
return function (target) {
console.log(str);
};
}
function decorator3(str) {
console.log("第三個函數(shù)運(yùn)行了");
return function (target) {
console.log(str);
};
}
let A = class A {};
A = __decorate(
[
decorator1("我是裝飾器1"), // 第一個函數(shù)的返回結(jié)果作為裝飾器
decorator2("我是裝飾器2"), // 第二個函數(shù)的返回結(jié)果作為裝飾器
decorator3("我是裝飾器3"), // 第三個函數(shù)的返回結(jié)果作為裝飾器
],
A
);結(jié)尾
如果讀完這篇文章能夠幫助你更好的理解類裝飾器,歡迎留言討論、點(diǎn)贊收藏。
以上就是淺談JS裝飾器以及裝飾器在TS中的使用方式的詳細(xì)內(nèi)容,更多關(guān)于JS裝飾器使用方法的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
一個超簡單的JS拖拽實(shí)現(xiàn)代碼(兼容IE,Firefox)
網(wǎng)上找的一個超簡單的JS拖拽,喜歡拖拽效果的朋友可以參考下。2010-04-04
javascript制作網(wǎng)頁圖片上實(shí)現(xiàn)下雨效果
這里給大家分享的是一則使用javascript實(shí)現(xiàn)在網(wǎng)頁圖片上下雨的特效,效果非常炫酷,推薦給小伙伴們。2015-02-02
uni-app?中清除定時器實(shí)現(xiàn)方法詳解
這篇文章主要為大家介紹了uni-app?中清除定時器實(shí)現(xiàn)方法詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-07-07
SelectPage v2.4 發(fā)布新增純下拉列表和關(guān)閉分頁功能
下拉分頁選擇插件 SelectPage v2.4 發(fā)布了,新增純下拉列表和關(guān)閉分頁功能,下面通過本文給大家啊介紹SelectPage的相關(guān)知識,感興趣的的朋友一起看看吧2017-09-09
JS獲取IE版本號與HTML設(shè)置IE文檔模式的方法
下面小編就為大家?guī)硪黄狫S獲取IE版本號與HTML設(shè)置IE文檔模式的方法。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2016-10-10
javascript實(shí)現(xiàn)簡單頁面倒計時
這篇文章主要為大家詳細(xì)介紹了javascript實(shí)現(xiàn)簡單頁面倒計時,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2021-03-03
JavaScript 注冊表訪問實(shí)現(xiàn)代碼
下面的代碼是讀取media palyer的默認(rèn)路徑。運(yùn)行后確認(rèn)即可,本程序沒有安全問題,大家放心測試。2009-07-07

