代码缺乏装饰?使用ts装饰器来装饰你的代码
TypeScript中的裝飾器
- 👏序言
- 😉一、類的裝飾器
- 1、什么是裝飾器
- 2、裝飾器的特點
- 3、幾種類的裝飾器
- (1)執行順序
- (2)參數判斷
- (3)裝飾器標準寫法
- 🤐二、類的其他裝飾器
- 1、方法裝飾器
- 2、訪問器的裝飾器
- 3、屬性的裝飾器
- 4、參數裝飾器
- 😐三、裝飾器實際使用的小例子
- 😏四、結束語
- 🥳 往期推薦
👏序言
在 ts 中,有一個經常被我們熟用但是又很少去注意的一個知識點,裝飾器。那在下文中,將講解類的裝飾器,一起類裝飾器中的幾種其他的裝飾器。
下面開始本文的講解~😜
😉一、類的裝飾器
1、什么是裝飾器
首先,我們先來講, TypeScript 中,類的裝飾器是什么。
裝飾器實際上是一種對類的修飾工具。比如說:某一天可能有個女孩子想要出去逛街,那么她可能會畫個美美的妝出門。因此,我們可以把裝飾器視為是化妝的這個過程,也就是一個美化的過程。
現在就是,假設我們有一個類,然后呢,要對它額外進行一些修飾,這個就是裝飾器要干的事情了。
2、裝飾器的特點
首先我們需要先來了解裝飾器的幾個特點。具體如下:
- 裝飾器本身就是一個函數;
- 裝飾器接收的參數是構造函數;
- 裝飾器通過 @ 符號來進行使用。
依據以上這幾個特點,下面我們來了解幾種類的裝飾器。
3、幾種類的裝飾器
(1)執行順序
// 第一個裝飾器 function testDecorator(constructor: any) {console.log('decorator'); }// 第二個裝飾器 function testDecorator1(constructor: any) {console.log('decorator1'); }// 裝飾器執行的時候,是從下到上,從右到左的順序 @testDecorator @testDecorator1 class Test {}const test = new Test(); // decorator1 decorator裝飾器執行的時候,是從下到上,從右到左的順序。
(2)參數判斷
我們如何讓類裝飾器接收一個參數呢?來看一段代碼:
// 外面再包一層函數 function testDecorator(flag: boolean) {// 工廠模式if (flag) {return function (constructor: any) {constructor.prototype.getName = () => {console.log('Monday');};};} else {return function (constructor: any) {};} }@testDecorator(true) class Test {}const test = new Test(); (test as any).getName(); // Monday通過上面這段代碼我們可以了解到,我們通過對類裝飾器的外部再包上一層函數,這其實有點像柯里化的形式,之后通過外部的這個函數進行傳參,也就是上面代碼中的 flag 。最終類裝飾器返回一個函數作為結果,順利地進行傳參。
(3)裝飾器標準寫法
上面的兩個裝飾器屬于兩個比較簡單和不太規范的裝飾器。下面我們來展現一種比較標準的寫法:
function testDecorator() {return function <T extends new (...args: any[]) => any>(constructor: T) {return class extends constructor {name = 'Tuesday';getName() {return this.name;}};}; }const Test = testDecorator()(class {name: string;constructor(name: string) {this.name = name;}} );const test = new Test('Monday'); console.log(test.getName()); // Tuesday在上面的代碼中, (...args: any[]) => any 是一個函數,返回值是一個對象的類型。這個函數會接收很多參數,函數把這些參數合并到一起,變成一個數組,也就是 ...args 。那 <T extends new (...args: any[]) => any> 是什么意思呢?意思是, T 可以通過 new (...args: any[]) => any 這種類型的構造函數,給實例化出來。所以 T 現在可以理解為是一個類或者是 constructor 這樣的一個構造函數。
最終,我們通過 testDecorator()() 這樣的方式,讓 test 實例可以訪問到 getName() 方法,并打印出 Tuesday 。
🤐二、類的其他裝飾器
1、方法裝飾器
這里我想要強調的一個問題是,大家覺得,類裝飾器的執行時刻是什么樣的?類裝飾器在類定義完成之后就可以立即對類進行一個裝飾。
那方法裝飾器,是什么樣的呢?
方法裝飾器,跟類裝飾器也是一樣的。它會等類創建好了之后,立即地把方法去做一個修改。
很多小伙伴可能會誤認為,我是不是得實例化的時候,才會對方法去做一個裝飾呢?其實不是這樣的,只要在定義完類以后,類就會幫助我們對類的方法去做一個裝飾。先來看一段代碼:
// 普通方法, target 對應的是類的 prototype // 靜態方法, target 對應的是類的 構造函數 function getNameDecorator(target: any,key: string,descriptor: PropertyDescriptor ) {// console.log(target);// descriptor的作用:對方法中的屬性做一些編輯descriptor.writable = true;// 通過調用 .value 的方式,可以對原來的方法做一些變更descriptor.value = function () {return 'decorator';}; }class Test {name: string;constructor(name: string) {this.name = name;}@getNameDecoratorgetName() {return this.name;} }const test = new Test('Monday'); test.getName = () => {return '123'; }; console.log(test.getName()); // decorator大家先看上面這段代碼,可能有的小伙伴會覺得,最終打印的是 123 。但其實,因為我們對方法進行了裝飾,所以最終打印的結果是 decorator 。
因此,一個裝飾器對一個方法做完裝飾之后,就可以多做很多事情了。包括原型target,key值 和 descriptor ,都可以對方法做很多修改。
2、訪問器的裝飾器
現在,我們來學習類里面中,訪問器的裝飾器。我們先來看一段代碼:
function visitDecorator(target: any,key: string,descriptor: PropertyDescriptor ) {// console.log(123); }class Test {private _name: string;constructor(name: string) {this._name = name;}// 這里不能寫@visitDecorator,同時寫兩個會引發報錯get name() {return this._name;}@visitDecoratorset name(name: string) {this._name = name;} }const test = new Test('Monday'); test.name = 'Tuesday'; console.log(test.name); // Tuesday其中, @visitDecorator 是一個訪問器裝飾器。我們現在來解釋下上面代碼中的運行路徑。
第一部分, test.name = 'Tuesday' 走的是 set 方法,把 Tuesday 這個值賦值給 name 。之后,等到我們運行 console.log 的時候,就是去調用 get 方法,所以最終打印出來的也就是 Tuesday 而不是 Monday 。
3、屬性的裝飾器
我們先來看第一種屬性的裝飾器。具體代碼如下:
function nameDecorator(target: any, key: string): any {const descriptor: PropertyDescriptor = {writable: true,};return descriptor; }class Test {@nameDecoratorname = 'Monday'; }const test = new Test(); test.name = 'Tuesday'; console.log(test.name); // Tuesday屬性裝飾器的寫法,也是一個 decorator 的形式,即上述代碼中的 @nameDecorator 。這個裝飾器接收兩個參數,分別是 原型target 和 屬性的名字key 。在這里我們可以返回一個 descriptor 來替換掉屬性原始的 descriptor 。替換完成之后,最終打印 Tuesday 。
繼續,我們來看第二種裝飾器。具體代碼如下:
// 該裝飾器無法直接修改實例上的屬性值(name),而只能修改原型上的屬性值(name) function nameDecorator(target: any, key: string): any {target[key] = 'Tuesday'; }// name存儲在類的實例上 class Test {@nameDecoratorname = 'Monday'; }const test = new Test(); test.name = 'Hello~'; console.log((test as any).name); // Hello~ console.log((test as any).__proto__.name); // Tuesday這種類型的裝飾器中,值得注意的點是, nameDecorator 只能用來修改原型上的屬性值,而無法直接修改實例上的屬性值。
4、參數裝飾器
上面我們講到了對類里面的方法、訪問器和屬性做修飾,現在,我們再來了解一種新的裝飾器:對類里面的方法中的參數做修飾。先來看一段代碼:
// 原型,方法名,參數所在的位置 function paramDecorator(target: any, key: string, paramIndex: number): any {console.log(target, key, paramIndex); // Test { getInfo: [Function] } , 'getInfo' , 1(參數所在位置是第2個位置) }class Test {getInfo(name: string, @paramDecorator age: number) {console.log(name, age);} }const test = new Test(); test.getInfo('Monday', 18); // Monday 18大家可以看到,通過對方法中的參數進行裝飾,我們可以獲取到裝飾器的原型,方法名和參數所在的位置,這個就是參數裝飾器。
😐三、裝飾器實際使用的小例子
上面我們講到了很多種裝飾器相關的原理知識,現在我們用一個實際使用的例子來帶大家更好的使用裝飾器。先看一段代碼:
const userInfo: any = undefined;function catchError(msg: string) {return function (target: any, key: string, descriptor: PropertyDescriptor) {const fn = descriptor.value;descriptor.value = function () {try {fn();} catch (e) {console.log(msg);}};}; }class Test {@catchError('userInfo.name 不存在')getName() {return userInfo.name;}@catchError('userInfo.age 不存在')getAge() {return userInfo.age;}@catchError('userInfo.gender 不存在')getGender() {return userInfo.gender;} }const test = new Test(); test.getName(); // userInfo.name 不存在 test.getAge(); // userInfo.age 不存在 test.getGender(); // userInfo.gender 不存在在上面的代碼中,我們做的是捕獲異常的一個功能。通過封裝 @catchError 裝飾器,來對我們最終使用的三個方法,getName 、 getAge 和 getGender ,對這三個方法進行異常捕獲。
以上算是對裝飾器的一次小小的實踐,后續深入學習可以再參考一些書籍去多練習。
😏四、結束語
在上面的文章中,我們講解了裝飾器中最基礎的類裝飾器,以及類裝飾器中的4中其他類型的裝飾器。最后,我們還用了一個小例子去簡單地了解了,裝飾器在實際應用中的一些操作。
到這里,關于裝飾器的學習就接近尾聲啦!不知道小伙伴們對裝飾器又有了一些新的了解呢?
如果您覺得這篇文章有幫助到您的的話不妨點贊支持一下喲~~😉
我們下期再見!👋👋👋
🥳 往期推薦
🛵TypeScript,從0到入門帶你進入類型的世界
總結
以上是生活随笔為你收集整理的代码缺乏装饰?使用ts装饰器来装饰你的代码的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: iPhone6完美越狱图文教程
- 下一篇: 探秘react,一文弄懂react的基本