摘要:值得注意的是,的返回值復(fù)寫了原始的構(gòu)造函數(shù),原因是類裝飾器必須返回一個(gè)構(gòu)造器函數(shù)。原始構(gòu)造函數(shù)的原型被復(fù)制給的原型,以確保在創(chuàng)建一個(gè)的新實(shí)例時(shí),操作符如愿以償,具體原因可參考鄙人另一篇文章原型與對(duì)象。
上一篇文章中,我們討論了TypeScript源碼中關(guān)于方法裝飾器的實(shí)現(xiàn),搞明白了如下幾個(gè)問(wèn)題:
裝飾器函數(shù)是如何被調(diào)用的?
裝飾器函數(shù)參數(shù)是如何傳入的?
__decorate函數(shù)干了些什么事情?
接下來(lái)我們繼續(xù)屬性裝飾器的觀察。
屬性裝飾器屬性裝飾器的聲明標(biāo)識(shí)如下:
declare type PropertyDecorator = (target: Object, propertyKey: string | symbol) => void;
如下我們?yōu)橐粋€(gè)類的屬性添加了一個(gè)名為@logProperty的裝飾器
class Person { @logProperty public name: string; public surname: string; constructor(name : string, surname : string) { this.name = name; this.surname = surname; } }
上一篇解釋過(guò),當(dāng)這段代碼最后被編譯成JavaScript執(zhí)行時(shí),方法__decorate會(huì)被調(diào)用,但此處會(huì)少最后一個(gè)參數(shù)(通過(guò)Object. getOwnPropertyDescriptor屬性描述符)
var Person = (function () { function Person(name, surname) { this.name = name; this.surname = surname; } __decorate( [logProperty], Person.prototype, "name" ); return Person; })();
需要注意的是,這次TypeScript編譯器并沒(méi)像方法裝飾器那樣,使用__decorate返回的結(jié)果覆蓋原始屬性。原因是屬性裝飾器并不需要返回什么。
Object.defineProperty(C.prototype, "foo", __decorate( [log], C.prototype, "foo", Object.getOwnPropertyDescriptor(C.prototype, "foo") ) );
那么,接下來(lái)具體實(shí)現(xiàn)這個(gè)@logProperty裝飾器
function logProperty(target: any, key: string) { // 屬性值 var _val = this[key]; // getter var getter = function () { console.log(`Get: ${key} => ${_val}`); return _val; }; // setter var setter = function (newVal) { console.log(`Set: ${key} => ${newVal}`); _val = newVal; }; // 刪除屬性 if (delete this[key]) { // 創(chuàng)建新的屬性 Object.defineProperty(target, key, { get: getter, set: setter, enumerable: true, configurable: true }); } }
實(shí)現(xiàn)過(guò)程首先聲明了一個(gè)變量_val,并用所裝飾的屬性值給它賦值(此處的this指向類的原型,key為屬性的名字)。
接著聲明了兩個(gè)方法getter和setter,由于函數(shù)是閉包創(chuàng)建的,所以在其中可以訪問(wèn)變量_val,在其中可以添加額外的自定義行為,這里添加了將屬性值打印在控制臺(tái)的操作。
然后使用delete操作符將原屬性從類的原型中刪除,不過(guò)需要注意的是:如果屬性存在不可配置的屬性時(shí),這里if(delete this[key])會(huì)返回false。而當(dāng)屬性被成功刪除,方法Object.defineProperty()將創(chuàng)建一個(gè)和原屬性同名的屬性,不同的是新的屬性getter和setter方法,使用上面新創(chuàng)建的。
至此,屬性裝飾器的實(shí)現(xiàn)就完成了,運(yùn)行結(jié)果如下:
var me = new Person("Remo", "Jansen"); // Set: name => Remo me.name = "Remo H."; // Set: name => Remo H. name; // Get: name Remo H.類裝飾器
類裝飾器的聲明標(biāo)識(shí)如下:
declare type ClassDecorator =(target: TFunction) => TFunction | void;
可以像如下方式使用類裝飾器:
@logClass class Person { public name: string; public surname: string; constructor(name : string, surname : string) { this.name = name; this.surname = surname; } }
和之前不同的是,經(jīng)過(guò)TypeScript編譯器編譯為JavaScript后,調(diào)用__decorate函數(shù)時(shí),與方法裝飾器相比少了后兩個(gè)參數(shù)。僅傳遞了Person而非Person.prototype。
var Person = (function () { function Person(name, surname) { this.name = name; this.surname = surname; } Person = __decorate( [logClass], Person ); return Person; })();
值得注意的是,__decorate的返回值復(fù)寫了原始的構(gòu)造函數(shù),原因是類裝飾器必須返回一個(gè)構(gòu)造器函數(shù)。接下來(lái)我們就來(lái)實(shí)現(xiàn)上面用到的類裝飾器@logClass:
function logClass(target: any) { // 保存對(duì)原始構(gòu)造函數(shù)的引用 var original = target; // 用來(lái)生成類實(shí)例的方法 function construct(constructor, args) { var c : any = function () { return constructor.apply(this, args); } c.prototype = constructor.prototype; return new c(); } // 新的構(gòu)造函數(shù) var f : any = function (...args) { console.log("New: " + original.name); return construct(original, args); } // 復(fù)制原型以便`intanceof`操作符可以使用 f.prototype = original.prototype; // 返回新的構(gòu)造函數(shù)(會(huì)覆蓋原有構(gòu)造函數(shù)) return f; }
這里實(shí)現(xiàn)的構(gòu)造器中,聲明了一個(gè)名為original的變量,并將所裝飾類的構(gòu)造函數(shù)賦值給它。接著聲明一個(gè)工具函數(shù)construct,用來(lái)創(chuàng)建類的實(shí)例。然后定義新的構(gòu)造函數(shù)f,在其中調(diào)用原來(lái)的構(gòu)造函數(shù)并將初始化的類名打印在控制臺(tái),當(dāng)然我們也可以添加一些其他自定義的行為。
原始構(gòu)造函數(shù)的原型被復(fù)制給f的原型,以確保在創(chuàng)建一個(gè)Person的新實(shí)例時(shí),instanceof操作符如愿以償,具體原因可參考鄙人另一篇文章原型與對(duì)象。
至此類裝飾器的實(shí)現(xiàn)就完成了,可以驗(yàn)證下:
var me = new Person("Remo", "Jansen"); // New: Person me instanceof Person; // true
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/101520.html
摘要:可見(jiàn)參數(shù)裝飾器函數(shù)需要個(gè)參數(shù)被裝飾類的原型,裝飾參數(shù)所屬的方法名,參數(shù)的索引。參數(shù)裝飾器不應(yīng)當(dāng)用來(lái)修改構(gòu)造器方法或?qū)傩缘男袨椋粦?yīng)當(dāng)用來(lái)產(chǎn)生某種元數(shù)據(jù)。一旦元數(shù)據(jù)被創(chuàng)建,我們便可以用其它的裝飾器去讀取它。 之前已經(jīng)分別介紹了方法裝飾器、屬性裝飾器和類裝飾器,這篇文章我們來(lái)繼續(xù)關(guān)注這些話題: 參數(shù)裝飾器 裝飾器工廠 我們將圍繞以下這個(gè)例子,來(lái)探討這些概念: class Person...
摘要:使用裝飾器的方法很簡(jiǎn)單在裝飾器名前加字符,寫在想要裝飾的方法上,類似寫注釋的方式裝飾器實(shí)際上是一個(gè)函數(shù),入?yún)樗b飾的方法,返回值為裝飾后的方法。經(jīng)過(guò)裝飾過(guò)的方法,它依然按照原來(lái)的方式執(zhí)行,只是額外執(zhí)行了附件的裝飾器函數(shù)的功能。 讓我來(lái)深入地了解一下TypeScript對(duì)于裝飾器模式的實(shí)現(xiàn),以及反射與依賴注入等相關(guān)特性。 在Typescript的源代碼中,可以看到裝飾器能用來(lái)修飾cla...
摘要:慶幸的是,已經(jīng)支持反射機(jī)制,來(lái)看看這個(gè)特性吧元數(shù)據(jù)反射可以通過(guò)安裝包來(lái)使用元數(shù)據(jù)反射的若要使用它,我們需要在中設(shè)置為,同時(shí)添加的引用,同時(shí)加載文件。復(fù)雜類型序列化的團(tuán)隊(duì)為復(fù)雜類型的元數(shù)據(jù)序列化做出了努力。 本篇內(nèi)容包括如下部分: 為什么JavaScript中需要反射 元數(shù)據(jù)反射API 基本類型序列化 復(fù)雜類型序列化 為什么JavaScript中需要反射? 關(guān)于反射的概念,摘自百度百...
摘要:本文從裝飾模式出發(fā),聊聊中的裝飾器和注解。該函數(shù)的函數(shù)名。不提供元數(shù)據(jù)的支持。中的元數(shù)據(jù)操作可以通過(guò)包來(lái)實(shí)現(xiàn)對(duì)于元數(shù)據(jù)的操作。 ??隨著Typescript的普及,在KOA2和nestjs等nodejs框架中經(jīng)常看到類似于java spring中注解的寫法。本文從裝飾模式出發(fā),聊聊Typescipt中的裝飾器和注解。 什么是裝飾者模式 Typescript中的裝飾器 Typescr...
摘要:看了這一章,發(fā)現(xiàn)原來(lái)是裝飾器,又一新知識(shí)。期間,裝飾器會(huì)做一些額外的工作。書中介紹了模塊中的三個(gè)裝飾器。另一個(gè)是,這個(gè)裝飾器把函數(shù)結(jié)果保存了起來(lái),避免傳入相同參數(shù)時(shí)重復(fù)計(jì)算。疊放不奇怪,裝飾器返回的就是函數(shù)或可調(diào)用對(duì)象。 在 Web 框架 Flask 中,最常看到的或許是以@app.route開(kāi)頭的那行代碼。由于還是剛接觸 Flask,所以對(duì)這種語(yǔ)法還不熟悉。看了這一章,發(fā)現(xiàn)原來(lái)是裝飾...
閱讀 825·2023-04-26 00:13
閱讀 2794·2021-11-23 10:08
閱讀 2432·2021-09-01 10:41
閱讀 2112·2021-08-27 16:25
閱讀 4177·2021-07-30 15:14
閱讀 2359·2019-08-30 15:54
閱讀 857·2019-08-29 16:22
閱讀 2736·2019-08-26 12:13