淺談JS裝飾器以及裝飾器在TS中的使用方式
類裝飾器
什么是類裝飾器呢?
類裝飾器的本質(zhì)是一個(gè)函數(shù)
,該函數(shù)接受一個(gè)參數(shù),表示類本身(構(gòu)造函數(shù)本身)
。 那么類裝飾器該如何使用呢?
function decorator (target) { } @decorator class A { }
這樣的話就完成了類裝飾器的編寫。不難看出類裝飾器的調(diào)用方式就是@函數(shù)名
的方式放在一個(gè)類的聲明之前。那如果在TS中使用的話,我們都知道TS中有類型檢查
,那對(duì)于裝飾器而言,主要在形參的類型上需要我們自己定義。那類裝飾器的形參表示的是類本身,該如何定義呢?
我們知道JS中的類其實(shí)就是一個(gè)函數(shù),所以我們可以使用Function
來(lái)對(duì)類進(jìn)行類型定義。但是這不是很嚴(yán)謹(jǐn),因?yàn)轭愂强梢杂?code>new關(guān)鍵字來(lái)聲明的,并且會(huì)返回一個(gè)object
,所以更推薦使用new (參數(shù)) => object
來(lái)定義類的類型。那么以上代碼就會(huì)被改造成這樣:
type decoratorType = new (...args:any[]) => object; function decorator ( target : decorarorType ) { }; @decorator; class A { }
值得注意的是,ts可能會(huì)在類名處有錯(cuò)誤提示,這是因?yàn)檠b飾器在ts中還處于試驗(yàn)階段。我們可以在 tsconfig.json
中配置experimentalDecorators
為true
來(lái)規(guī)避這個(gè)報(bào)錯(cuò)。
類裝飾器的運(yùn)行時(shí)機(jī)
類裝飾器的運(yùn)行時(shí)間是在類定義后直接運(yùn)行
。我們可以驗(yàn)證一下:
運(yùn)行后發(fā)現(xiàn),在class A
定義完成后立即就輸出了我是decorator
。
從編譯結(jié)果上也能很容易的看出,裝飾器
是在類定義后直接調(diào)用的:
// 這里有一個(gè)函數(shù) 他的第一個(gè)參數(shù)decorators表示裝飾器數(shù)組,第二個(gè)參數(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í)我們定義的裝飾器函數(shù) function decotator(target) { console.log("我是decorator"); } // 定義類 let A = class A {}; // 裝飾器和類作為參數(shù)傳入__decorate中運(yùn)行。 A = __decorate([decotator], A);
裝飾器的返回值
裝飾器可以有以下幾種返回值:
- void
- 一個(gè)新的類:會(huì)將新的類替換掉裝飾目標(biāo)
這里具體演示第二種返回值----返回一個(gè)新的類。在裝飾器中返回新的類class B
。此時(shí)new一個(gè)class A
的實(shí)例,打印后發(fā)現(xiàn)為class B
的實(shí)例對(duì)象。說(shuō)明class A
通過裝飾器已經(jīng)被替換為class B
了。
所以說(shuō)在裝飾器中可以通過以上方式,增強(qiáng)被裝飾類的功能。不過在ts
中這種方式可能丟失一些類型檢查。如下圖:
這是因?yàn)檠b飾器是一個(gè)通用的,雖然在當(dāng)前場(chǎng)景下因?yàn)?code>class B繼承了裝飾器傳入的class A
,因?yàn)轭愌b飾器的入?yún)⑹?strong>動(dòng)態(tài)的,所以ts并不能知道到底有沒有prop1
這個(gè)屬性。當(dāng)然這樣并不會(huì)影響代碼的功能,只是在ts中會(huì)丟失類型檢查。
那么如果我想讓裝飾器能夠接受一些額外的內(nèi)容,該怎么做呢?在實(shí)際開發(fā)中,可能需要某些數(shù)據(jù)來(lái)參與邏輯。那么我們可以用以下方式來(lái)實(shí)現(xiàn):
type decoratorType = new (...args: any) => object; function decorator(str: string) { return function (target: decoratorType) { console.log(str); console.log(target); }; } @decorator("這是一個(gè)類") class A {}
因?yàn)樾枰邮茴~外的信息,所以這必然是需要一個(gè)函數(shù)調(diào)用的形式。這里 @decorator
接受了一個(gè)字符串參數(shù)。并且decorator
函數(shù)接受一個(gè)形參str
且返回一個(gè)新的函數(shù),所以decorator
函數(shù)返回的函數(shù)會(huì)作為真正的裝飾器,它可以接收到被修飾類class A
。我們可以看一下運(yùn)行結(jié)果。
多個(gè)裝飾器的情況
那么如果有多個(gè)裝飾器呢?他們的運(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,所以會(huì)按順序執(zhí)行,事實(shí)真的是這樣嗎?讓我們來(lái)看一下運(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í)際長(zhǎng)度) 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; };
那如果把裝飾器作為一個(gè)函數(shù)來(lái)調(diào)用呢?
type decoratorType = new (...args: any) => object; function decorator1(str: string) { console.log("第一個(gè)函數(shù)運(yùn)行了"); return function (target: decoratorType) { console.log(str); }; } function decorator2(str: string) { console.log("第二個(gè)函數(shù)運(yùn)行了"); return function (target: decoratorType) { console.log(str); }; } function decorator3(str: string) { console.log("第三個(gè)函數(shù)運(yùn)行了"); return function (target: decoratorType) { console.log(str); }; } @decorator1("我是裝飾器1") @decorator2("我是裝飾器2") @decorator3("我是裝飾器3") class A {}
當(dāng)作為函數(shù)調(diào)用時(shí),會(huì)先執(zhí)行函數(shù)體
,因?yàn)樵摵瘮?shù)又返回了一個(gè)函數(shù)
,所以返回的新函數(shù)會(huì)作為裝飾器
。運(yùn)行第一個(gè)函數(shù)會(huì)的到第一個(gè)裝飾器,以此類推會(huì)獲得三個(gè)裝飾器。而裝飾器是按照后加入先調(diào)用
的形式,所以會(huì)輸出以下結(jié)果:
從編譯結(jié)果來(lái)看能更好的理解:
function decorator1(str) { console.log("第一個(gè)函數(shù)運(yùn)行了"); return function (target) { console.log(str); }; } function decorator2(str) { console.log("第二個(gè)函數(shù)運(yùn)行了"); return function (target) { console.log(str); }; } function decorator3(str) { console.log("第三個(gè)函數(shù)運(yùn)行了"); return function (target) { console.log(str); }; } let A = class A {}; A = __decorate( [ decorator1("我是裝飾器1"), // 第一個(gè)函數(shù)的返回結(jié)果作為裝飾器 decorator2("我是裝飾器2"), // 第二個(gè)函數(shù)的返回結(jié)果作為裝飾器 decorator3("我是裝飾器3"), // 第三個(gè)函數(shù)的返回結(jié)果作為裝飾器 ], A );
結(jié)尾
如果讀完這篇文章能夠幫助你更好的理解類裝飾器,歡迎留言討論、點(diǎn)贊收藏。
以上就是淺談JS裝飾器以及裝飾器在TS中的使用方式的詳細(xì)內(nèi)容,更多關(guān)于JS裝飾器使用方法的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
JS獲取時(shí)間的相關(guān)函數(shù)及時(shí)間戳與時(shí)間日期之間的轉(zhuǎn)換
時(shí)間戳和時(shí)間日期的轉(zhuǎn)換是常見的操作,下面就通過代碼實(shí)例介紹一下如何實(shí)現(xiàn)它們之間的相互轉(zhuǎn)換,感興趣的朋友一起學(xué)習(xí)吧2016-02-02JavaScript實(shí)現(xiàn)計(jì)數(shù)器基礎(chǔ)方法
這篇文章主要為大家詳細(xì)介紹了JavaScript實(shí)現(xiàn)計(jì)數(shù)器的基礎(chǔ)方法2017-10-10
,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下javascript導(dǎo)出csv文件(excel)的方法示例
這篇文章主要給大家介紹了關(guān)于javascript導(dǎo)出csv文件(excel)的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用javascript具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-08-08詳解js location.href和window.open的幾種用法和區(qū)別
這篇文章主要介紹了詳解js location.href和window.open的幾種用法和區(qū)別,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-12-12