喜歡 JS Decorators 那點事

曾經(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é)束了。

最后給幾個不錯的開源項目 拜讀一下大師怎么具體使用的 :

Core Decorators

Lo-Dash Decorators

Routing Controllers

TypeDI?– Dependency Injection based on Typescript and Decorators

TypeORM?– ORM based on Typescript and Decorators

Class Validator

Class Transformer

由于是第一次寫東西. 寫的不好的地方 請多多指教曼验。 希望對大家有幫助 ?

Fred ?

奧克蘭

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末逆害,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子蚣驼,更是在濱河造成了極大的恐慌展哭,老刑警劉巖票腰,帶你破解...
    沈念sama閱讀 219,427評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件肌访,死亡現(xiàn)場離奇詭異政溃,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,551評論 3 395
  • 文/潘曉璐 我一進店門翼抠,熙熙樓的掌柜王于貴愁眉苦臉地迎上來咙轩,“玉大人,你說我怎么就攤上這事阴颖』詈埃” “怎么了?”我有些...
    開封第一講書人閱讀 165,747評論 0 356
  • 文/不壞的土叔 我叫張陵量愧,是天一觀的道長钾菊。 經(jīng)常有香客問我,道長偎肃,這世上最難降的妖魔是什么煞烫? 我笑而不...
    開封第一講書人閱讀 58,939評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮累颂,結(jié)果婚禮上滞详,老公的妹妹穿的比我還像新娘。我一直安慰自己紊馏,他們只是感情好料饥,可當我...
    茶點故事閱讀 67,955評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著朱监,像睡著了一般稀火。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上赌朋,一...
    開封第一講書人閱讀 51,737評論 1 305
  • 那天,我揣著相機與錄音篇裁,去河邊找鬼沛慢。 笑死,一個胖子當著我的面吹牛达布,可吹牛的內(nèi)容都是我干的团甲。 我是一名探鬼主播,決...
    沈念sama閱讀 40,448評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼黍聂,長吁一口氣:“原來是場噩夢啊……” “哼躺苦!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起产还,我...
    開封第一講書人閱讀 39,352評論 0 276
  • 序言:老撾萬榮一對情侶失蹤匹厘,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后脐区,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體愈诚,經(jīng)...
    沈念sama閱讀 45,834評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,992評論 3 338
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了炕柔。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片酌泰。...
    茶點故事閱讀 40,133評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖匕累,靈堂內(nèi)的尸體忽然破棺而出陵刹,到底是詐尸還是另有隱情,我是刑警寧澤欢嘿,帶...
    沈念sama閱讀 35,815評論 5 346
  • 正文 年R本政府宣布衰琐,位于F島的核電站,受9級特大地震影響际插,放射性物質(zhì)發(fā)生泄漏碘耳。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,477評論 3 331
  • 文/蒙蒙 一框弛、第九天 我趴在偏房一處隱蔽的房頂上張望辛辨。 院中可真熱鬧,春花似錦瑟枫、人聲如沸斗搞。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,022評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽僻焚。三九已至,卻和暖如春膝擂,著一層夾襖步出監(jiān)牢的瞬間虑啤,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,147評論 1 272
  • 我被黑心中介騙來泰國打工架馋, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留狞山,地道東北人。 一個月前我還...
    沈念sama閱讀 48,398評論 3 373
  • 正文 我出身青樓叉寂,卻偏偏與公主長得像萍启,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子屏鳍,可洞房花燭夜當晚...
    茶點故事閱讀 45,077評論 2 355

推薦閱讀更多精彩內(nèi)容

  • 什么是ES6勘纯? ECMAScript 6.0 是繼ECMAScript 5.1 之后 JavaScript 語...
    多多醬_DuoDuo_閱讀 1,098評論 0 4
  • *node下用express框架,實現(xiàn)一個簡單的mvc *構(gòu)建工具:gulp / babel / webpack ...
    韓娜愛吃辣_前端程序媛閱讀 1,093評論 0 1
  • 寫在前面钓瞭,因為function存在變量提升驳遵,所以修飾器是只能修飾類,而不能修飾函數(shù) 修飾器是一個函數(shù),用來修改類的...
    ITtian閱讀 1,913評論 0 3
  • 天使不會在同一個地方逗留太長時間,因為她有對翅膀.. 摘下翅膀的天使還是天使嗎? 沒有了翅膀,她不能飛翔. 人是只...
    江吳童閱讀 702評論 0 4
  • 筆記 強中自有強中手山涡,一山還比一山高超埋。當我們放眼世界后搏讶,才看清自己的真正能力和位...
    笨笨姐妹閱讀 137評論 0 0