曾經(jīng)一直在嵌入式數(shù)通領(lǐng)域混跡著,之后移民到新西蘭發(fā)現(xiàn)這些都沒有了用武之地砖织。兩個月前決定開始學習前端瑟捣,看了很多新的知識點。當然JS 是不得不學的一個語言嚎卫,今天就講講Decorators的運用嘉栓。在這篇文字里,我將一點點的抽絲剝繭來闡述Decorators驰凛,以至于一點點的把它的美展現(xiàn)出來胸懈。
如果你有過Python 的深厚背景的話,這個語法和思想應(yīng)該是一見如故的感覺恰响。學起來相對會簡單很多趣钱。但是對于我這種只有過C背景的菜鳥來說 相對有點難度。不過要感謝?w3Schools胚宦。很多的語言的網(wǎng)站開發(fā)的語言概述 我基本上都是從上面看的首有。查了一下?Decorators 應(yīng)該是 Typescript 里面的概念。
我一開始直接學習的 是 RN枢劝,所以對 typescript 和 Angular2 并沒有太大的感覺 井联。但是當我讀了不少代碼的時候 我慢慢的發(fā)現(xiàn)很多的庫文件都開始用typescript來編寫了。還是那么的優(yōu)美 ?我感覺是時候要來學習他們了您旁。上周五 我非常好奇的學習了這個在ES7里面的新功能 Decorators. 我發(fā)現(xiàn)我有點迷戀上了它烙常。它性感迷人的以至于我們不得不想方設(shè)法的把它運用到個個地方. ?因為沒有發(fā)現(xiàn)好的文章去寫它,所以我決定把我學習的歷程寫出來,希望對想學習的朋友們有所幫助蚕脏。
首先:Decorators 到底是一個什么東西侦副?
下圖是我從網(wǎng)上查到的 Decorators pattern 的一些信息:
是不是有點抽象? 那我們來個具體的模型 :
WeaponAccessory 在這里就是一個 decorator. ? 他們給BaseWeapon 增加了額外的功. 如果還沒有看明白 不用緊張 我第一遍也沒看明白驼鞭。 多看幾遍就好了秦驯。這里我們先給Decorators下一個定義: ?Decorators are functions that give additional super-powers to different function。從這個定義中我們獲知: 1. Decorators 是功能 ?2. 這個功能是增強了額外的超能量 給被修飾的功能挣棕。想想也是 ?Decorators的中文意思 就是 修飾译隘,裝修的意思。裝修一個東西 就是讓這個東西更好看 更美麗 更實用洛心。是吧固耘?
一句話: 就把它認為是一個object的一次外包裝。
1. ?實際的 object 是被 wrapper 封裝住的
2. 如果你想訪問一個object 词身,那你一定要先訪問 它的 封裝wrapper
Decorators 讓我們可以注釋和更改類以及類的屬性 再設(shè)計階段?
請注意:?Decorators 僅僅只是函數(shù)
其次:我們到底怎么運用它呢玻驻?
Decorators 是在ES7里面提出的一個功能點 ?在這里我們將使用Babel 和 Typescript 來實現(xiàn)的編譯器來實現(xiàn)他們 (在本文中 我將用 Typescript )
1. 怎么設(shè)置?
在你的 tsconfig.json 這個文件里面, 你應(yīng)該把 ?experimentalDecorators 和 emitDecoratorMetadata 設(shè)置為 true.
{"compilerOptions":{
"module":"commonjs",
"experimentalDecorators":true,
"emitDecoratorMetadata":true,
"target":"es2015"
},
"exclude":[
????"node_modules"]
}
2. ?閉嘴,觀察
為了讓事情簡單話偿枕,我一直遵循這一個學習方法那就是:少說多做. ?我會不管三七二十一先不停的寫代碼,關(guān)注輸出户辫,然后再去思考?
function leDecorator(target, propertyKey: string, descriptor: PropertyDescriptor): any {
? ? var oldValue = descriptor.value;
? ? descriptor.value = function() {
? ? ? console.log(`Calling "${propertyKey}" with`, arguments,target);
? ? ? let value = oldValue.apply(null, [arguments[1], arguments[0]]);
? ? ? console.log(`Function is executed`);
? ? ? return value + "; This is awesome";
? ? };
? ? return descriptor;
? }
? class JSMeetup {
? ? speaker = "Ruban";
? ? //@leDecorator
? ? welcome(arg1, arg2) {
? ? ? console.log(`Arguments Received are ${arg1} ${arg2}`);
? ? ? return `${arg1} ${arg2}`;
? ? }
? }
? const meetup = new JSMeetup();
? console.log(meetup.welcome("World", "Hello"));
一旦你運行了上面的代碼 他們的輸出會是: ?
Arguments Received are World Hello?
World Hello
現(xiàn)在把代碼里面的// 去掉渐夸,再運行它結(jié)果會是:?
Calling "welcome" with { '0': 'World', '1': 'Hello' } JSMeetup {}
?Arguments Received are Hello World
?Function is executed Hello World;?
This is awesome
是不是覺得很神奇?你只是用Decorator 注釋了一個函數(shù)渔欢,結(jié)果就截然不同了
這到底為什么呢墓塌? 讓我們一起揭開他們的面紗吧。
?Decorators的類型:
1.方法修飾( Method Decorator ?)
2. 屬性修飾(Property Decorators )
3. ?類修飾(Class Decorator)
4. 參數(shù)修飾(Parameter Decorator ?)
好了 不要急奥额, 讓我們一個個的來揭示其中的奧妙吧苫幢。(為了方便下面標題只寫英文)
Method Decorators?
Method Decorators是第一個我們將 要 一起學習的修飾類型. 我們將會學習到它的一些本質(zhì)。通過使用Method Decorators 我們將會學會很好的掌控輸入函數(shù)和輸出函數(shù).?
修飾簽名(Decorator Signature)
MethodDecorator = <T>(target: Object, key: string, descriptor: TypedPropertyDescriptor<T>) => TypedPropertyDescriptor<T> | Void;
前件:
當我們學習寫第一個Decorators垫挨,讓我們一起學習一下基本知識:.
理解Decorators的參數(shù)
1.?target -> 被修飾的對象
2. key -> 被修飾方法的名字
3 ?descriptor -> 給定的屬性的標識符. ?你可以看到這個屬性標識符通過調(diào)用 這個函數(shù): functionObject.getOwnPropertyDescriptor()?
Property Descriptors:?
看下面的輸出結(jié)果 可以更好的有一個直觀的概念:
著這里我們定義 一個object 韩肝,命名為o,其包含屬性:??foo?,?bar 和 foobar.? 然后我們打印每一個屬性的 屬性描述標識 用?Object.getOwnPropertyDescriptor(). ?在這里讓我們快速過一下value?,?enumerable?,?configurable 和 writable 的意思.
value – > 實際的數(shù)值 或者函數(shù) 被這個object 屬性擁有的
enumerable -> 是否這么屬性可以被枚舉九榔“Ь可能在便利的時候會被用到
configurable – >這個屬性是否可以被配置
writable -> 這個屬性是否可以被寫入
正如我們所發(fā)現(xiàn),所有的屬性和方法都有他們自己獨有的描述哲泊。Decorators會更改在這個描述去添加額外的功能剩蟀。當我們知道了怎么更改和定義這些屬性的時候,我們就可以用Decorators 去做一些改變了
實例( Method?Decorator)
在下面的例子里切威,我們將去更改輸入和輸出的方法:
function leDecorator(target, propertyKey: string, descriptor: PropertyDescriptor): any {
? ? var oldValue = descriptor.value;
? ? descriptor.value = function() {
? ? ? console.log(`Calling "${propertyKey}" with`, arguments,target);
? ? ? // Executing the original function interchanging the arguments
? ? ? let value = oldValue.apply(null, [arguments[1], arguments[0]]);
? ? ? //returning a modified value
? ? ? return value + "; This is awesome";
? ? };
? ? return descriptor;
? }
? class JSMeetup {
? ? speaker = "Ruban";
? ? //@leDecorator
? ? welcome(arg1, arg2) {
? ? ? console.log(`Arguments Received are ${arg1}, ${arg2}`);
? ? ? return `${arg1} ${arg2}`;
? ? }
? }
? const meetup = new JSMeetup();
? console.log(meetup.welcome("World", "Hello"));
如果我們run 上面的代碼(Decorators被注釋掉的情況下), 輸出如下:
Arguments Received are World, Hello
World Hello
但是當我們使用Decorators的時候(去掉//):?
Arguments Received are Hello, World
Hello World; This is awesome
到底發(fā)生了什么?
在這個修飾函數(shù)中育特,這個描述標識 包含了與它相關(guān)的特定的屬性(前面我們講過). 首先,我們把原始函數(shù)保存到一個變量中 ?(var oldValue = descriptor.value;?) . 然后先朦,我們更改這個描述標識中的值缰冤,然后把它返回. 正如我們所見的更改函數(shù)犬缨,我們執(zhí)行了原始函數(shù)但是更改了參數(shù),同時返回了更改結(jié)果.?
是不是很神奇锋谐?
Decorators 包裝了我們的方法. 通過使用Decorators, 我們基本上有能力掌控輸入哈數(shù)和輸出函數(shù). 我們是不是很有魔法遍尺? 哈哈
需要深思的幾點:
1. Decorators 被調(diào)用是Class 被聲明的時候,而不是被實例化的時候涮拗。要是后者的話 那就是虛函數(shù)了 ?哈哈 ? C++的?
2. 函數(shù)Decorators 會返回一個值
3. 正如上面的例子乾戏,最好是先保存存在的標識值,然后返回一個新的三热。原因是當多Decorators的時候 其他的Decorators 有可能無法訪問原始的東西
4. 不要使用箭頭語法當設(shè)置標識里面的變量的時候
我們能用Decorators 做點什么呢?
1. 做Log
2. 格式化
3. ?權(quán)限檢測
4. ?屏蔽復(fù)寫的函數(shù)?
5. 打時間戳
6. 流量限制
哈哈 當然 可能還有很多其他的用法鼓择。我們可以繼續(xù)追加。就漾。呐能。。抑堡。
在我們結(jié)束之前 我們再過一遍主題思想:?
Decorator Factory
我們可以通過使用 decorator factories 去定制化的實現(xiàn)我們想要的功能 :
function leDecoratorFactory(randomData: string) {
? ? return (target, propertyKey: string, descriptor: PropertyDescriptor): any => {
? ? ? var oldValue = descriptor.value;
? ? ? descriptor.value = function () {
? ? ? ? console.log(`Calling "${propertyKey}" with`, arguments, target);
? ? ? ? let value = oldValue.apply(null, [arguments[1], arguments[0]]);
? ? ? ? console.log(`Function is executed`);
? ? ? ? return value + "; This is awesome; " + randomData;
? ? ? }
? ? ? return descriptor;
? ? }
? }
? class JSMeetup {
? ? speaker = "Ruban";
? ? @leDecoratorFactory("Extra Data")
? ? welcome(arg1, arg2) {
? ? ? console.log(`Arguments Received are ${arg1} ${arg2}`);
? ? ? return `${arg1} ${arg2}`;
? ? }
? }
? const meetup = new JSMeetup();
? console.log(meetup.welcome("World", "Hello"));
上面的代碼的輸出如下:(仔細看看 和前一個例子有什么微小的差別摆出,找到了嗎? cheer!!!!!!!!!!!!)
Arguments Received are Hello World
Hello World; This is awesome; Extra Data
總結(jié)?
Decorator Factory 可以接受用戶的參數(shù)并且返回一個修飾函數(shù).
我們可以不用再去創(chuàng)建相同的修飾函數(shù)了
我們可以通過一個點去訪問多個不同的Decorators
重點:
Decorators 是從上到下的評估
Decorators 是從下到上的執(zhí)行
Property Decorators
Property decorators 和上面講的方法修飾很相似. 我們可以通過她去從新定義 getters首妖,setters, 還有其他的屬性類似枚舉偎漫,配置等
修飾簽名:
PropertyDecorator = (target: Object, key: string) => void;
前提條件:
為了更好的禮節(jié)和寫屬性修飾,我們需要深入了解?Object.defineProperty
思考點:
1. 沒有返回值
?2. defineProperty的使用
用途:
1. 更改數(shù)據(jù)
2. 驗證合法性
3. 格式化
Class Decorators
Class Decorators 基本上是更改類的構(gòu)造函數(shù). 通過使用類修飾有缆,我們可以更改或者添加新的屬性象踊,方法 到類里面去
修飾簽名:?
ClassDecorator = <TFunction extends Function>(target: TFunction) => TFunction;
例子:?
function AwesomeMeetup<T extends { new (...args: any[]): {} }>(constructor: T) {
? ? return class extends constructor implements extra {
? ? ? speaker: string = "Ragularuban";
? ? ? extra = "Tadah!";
? ? }
? }
? //@AwesomeMeetup
? class JSMeetup {
? ? public speaker = "Ruban";
? ? constructor() {
? ? }
? ? greet() {
? ? ? return "Hi, I'm " + this.speaker;
? ? }
? }
? interface extra {
? ? extra: string;
? }
? const meetup = new JSMeetup() as JSMeetup & extra;
? console.log(meetup.greet());
? console.log(meetup.extra);
如果不開啟修飾類的話 結(jié)果如下:
Hi, I’m Ruban
undefined
開啟修飾類的結(jié)果如下:
Hi, I’m Ragularuban
Tadah!
由此可見類修飾接受一個參數(shù),就是它自己. 我們可以通過它更改這個類 .
思考點:
當原始函數(shù)的構(gòu)造函數(shù)被調(diào)用的時候 這個擴展類就會被調(diào)用
場景:
記錄
任何你想更改的東西
Parameter Decorator:
到目前為止棚壁,我們可以認為參數(shù)修飾是被用作改變函數(shù)的參數(shù)杯矩。但是 還是有點小不同的 ?
它可以用來標記需要受注意的參數(shù). 我們標記這些參數(shù),然后用函數(shù)修飾去做動作.
修飾簽名:?
ParameterDecorator = (target: Object, propertyKey: string, parameterIndex: number) => void;
例子:
function logParameter(target: any, key: string, index: number) {
? ? var metadataKey = `myMetaData`;
? ? if (Array.isArray(target[metadataKey])) {
? ? ? target[metadataKey].push(index);
? ? }
? ? else {
? ? ? target[metadataKey] = [index];
? ? }
? }
? function logMethod(target, key: string, descriptor: any): any {
? ? var originalMethod = descriptor.value;
? ? descriptor.value = function (...args: any[]) {
? ? ? var metadataKey = `myMetaData`;
? ? ? var indices = target[metadataKey];
? ? ? console.log('indices', indices);
? ? ? for (var i = 0; i < args.length; i++) {
? ? ? ? if (indices.indexOf(i) !== -1) {
? ? ? ? ? console.log("Found a marked parameter at index" + i);
? ? ? ? ? args[i] = "Abrakadabra";
? ? ? ? }
? ? ? }
? ? ? var result = originalMethod.apply(this, args);
? ? ? return result;
? ? }
? ? return descriptor;
? }
? class JSMeetup {
? ? //@logMethod
? ? public saySomething(something: string, @logParameter somethingElse: string): string {
? ? ? return something + " : " + somethingElse;
? ? }
? }
? let meetup = new JSMeetup();
? console.log(meetup.saySomething("something", "Something Else"));
沒有修飾的結(jié)果:
something : Something Else
修飾后的結(jié)果:
something : Abrakadabra
發(fā)生了什么?
Parameter Decorator 有三個參數(shù)(target, key,index). ?在參數(shù)修飾中 , 我們基本上標記出參數(shù)再一個數(shù)組中 (myMetaData?).?
然后袖外,我們使用函數(shù)修飾來讀取這些標識的參數(shù)(in?myMetaData)史隆,最后更改這些參數(shù) ?
思考點:
沒有返回值
通常不不能單獨使用
使用:
依賴注入
標記參數(shù)然后進行判斷合法性
好了 今天就到此結(jié)束了。
最后給幾個不錯的開源項目 拜讀一下大師怎么具體使用的 :
TypeDI?– Dependency Injection based on Typescript and Decorators
TypeORM?– ORM based on Typescript and Decorators
由于是第一次寫東西. 寫的不好的地方 請多多指教曼验。 希望對大家有幫助 ?
Fred ?
奧克蘭