淺談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),因為類是可以用new關(guān)鍵字來聲明的,并且會返回一個object,所以更推薦使用new (參數(shù)) => object來定義類的類型。那么以上代碼就會被改造成這樣:
type decoratorType = new (...args:any[]) => object;
function decorator ( target : decorarorType ) {
};
@decorator;
class A {
}值得注意的是,ts可能會在類名處有錯誤提示,這是因為裝飾器在ts中還處于試驗階段。我們可以在 tsconfig.json中配置experimentalDecorators為true來規(guī)避這個報錯。
類裝飾器的運(yùn)行時機(jī)
類裝飾器的運(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中這種方式可能丟失一些類型檢查。如下圖:

這是因為裝飾器是一個通用的,雖然在當(dāng)前場景下因為class B繼承了裝飾器傳入的class A,因為類裝飾器的入?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 {}因為需要接受額外的信息,所以這必然是需要一個函數(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 {}按照我們一貫的思維,因為是同步代碼,所以會按順序執(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)用裝飾器(從后往前因為是i--且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ù)體,因為該函數(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獲取時間的相關(guān)函數(shù)及時間戳與時間日期之間的轉(zhuǎn)換
時間戳和時間日期的轉(zhuǎn)換是常見的操作,下面就通過代碼實(shí)例介紹一下如何實(shí)現(xiàn)它們之間的相互轉(zhuǎn)換,感興趣的朋友一起學(xué)習(xí)吧2016-02-02
JavaScript實(shí)現(xiàn)計數(shù)器基礎(chǔ)方法
這篇文章主要為大家詳細(xì)介紹了JavaScript實(shí)現(xiàn)計數(shù)器的基礎(chǔ)方法2017-10-10
,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
javascript導(dǎo)出csv文件(excel)的方法示例
這篇文章主要給大家介紹了關(guān)于javascript導(dǎo)出csv文件(excel)的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家學(xué)習(xí)或者使用javascript具有一定的參考學(xué)習(xí)價值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧2019-08-08
詳解js location.href和window.open的幾種用法和區(qū)別
這篇文章主要介紹了詳解js location.href和window.open的幾種用法和區(qū)別,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-12-12

