當前位置:
首頁 > 知識 > Class:向傳統類模式轉變的構造函數

Class:向傳統類模式轉變的構造函數

前言

JS基於原型的『類』,一直被轉行前端的碼僚們大呼驚奇,但接近傳統模式使用 關鍵字定義的出現,卻使得一些前端同行深感遺憾而紛紛留言:「還我獨特的JS」、「凈搞些沒實質的東西」、「自己沒有類還非要往別家的類上靠」,甚至是「已轉行」等等。有情緒很正常,畢竟新知識意味著更多時間與精力的開銷,又不是簡單的閉眼享受。

然而歷史的軸印前行依舊,對於 可以肯定的一點是你不能對面試官說:「拜託,不是小弟不懂,僅僅是不願意了解,您換個問題唄!」一方面雖然 只是個語法糖,但 對繼承的改進還是不錯的。另一方面今後可能在『類』上出現的新特性應該是由 而不是構造函數承載,誰也不確定它將來會出落得怎樣標緻。因此,來來來,慢慢的喝下這碗熱氣騰騰的紅糖薑湯。


1 class

ECMAScript中沒有類的概念,我們的實例是基於原型由構造函數生成具有動態屬性和方法的對象。不過為了與國際接軌,描述的更為簡便和高大上,依然會使用『類』這一詞。所以JS的類等同於構造函數。ES6的 只是個語法糖,其定義生成的對象依然構造函數。不過為了與構造函數模式區分開,我們稱其為類模式。學習 需要有構造函數和原型對象的知識,具體可以自行百度。

1.1 與變數對比

關鍵字 類似定義函數的關鍵字 ,其定義的方式有聲明式和表達式(匿名式和命名式)兩種。通過聲明式定義的變數的性質與 不同,更為類似 和 ,不會提前解析,不存在變數提升,不與全局作用域掛鉤和擁有暫時性死區等。 定義生成的變數就是一個構造函數,也因此,類可以寫成立即執行的模式。


1.2 與對象對比

類內容( 裡面)的形式與對象字面量相似。不過類內容裡面只能定義方法不能定義屬性,方法的形式只能是函數簡寫式,方法間不用也不能用逗號分隔。方法名可以是帶括弧的表達式,也可以為 值。方法分為三類,構造方法( 方法)、原型方法(存在於構造函數的 屬性上)和靜態方法(存在於構造函數本身上)

不能直接定義屬性,並不表示類不能有原型或靜態屬性。解析 會形成一個構造函數,因此只需像為構造函數添加屬性一樣為類添加即可。更為直接也是推薦的是只使用 函數定義只讀屬性。為什麼不能直接設置屬性?是技術不成熟?是官方希望傳遞某種思想?抑或僅僅是筆者隨意拋出的一個問題?


1.3 與構造函數對比

下面是使用構造函數和類實現相同功能的代碼。直觀上, 簡化了代碼,使得內容更為聚合。 方法體等同構造函數的函數體,如果沒有顯式定義此方法,一個空的 方法會被默認添加用於返回新的實例。與ES5一樣,也可以自定義返回另一個對象而不是新實例。

類雖然是個函數,但只能通過 生成實例而不能直接調用。類內部所定義的全部方法是不可枚舉的,在構造函數本身和 上添加的屬性和方法是可枚舉的。類內部定義的方法默認是嚴格模式,無需顯式聲明。以上三點增加了類的嚴謹性,比較遺憾的是,依然還沒有直接定義私有屬性和方法的方式。

在方法前加上 關鍵字表示此方法為靜態方法,它存在於類本身,不能被實例直接訪問。靜態方法中的 指向類本身。因為處於不同對象上,靜態方法和原型方法可以重名。ES6新增了一個命令 ,指代 後面的構造函數或 ,該命令的使用有某些限制,具體請看下面示例。


2 extends

ES5中的經典繼承方法是寄生組合式繼承,子類會分別繼承父類實例和原型上的屬性和方法。ES6中的繼承本質也是如此,不過實現方式有所改變,具體如下面的代碼。可以看到,原型上的繼承是使用 關鍵字這一更接近傳統語言的形式,實例上的繼承是通過調用 完成子類 塑造。表面上看,方式更為的統一和簡潔。


2.1 與構造函數對比

使用 繼承,不僅僅會將子類的 屬性的原型對象( )設置為父類的 ,還會將子類本身的原型對象( )設置為父類本身。這意味著子類不單單會繼承父類的原型數據,也會繼承父類本身擁有的靜態屬性和方法。而ES5的經典繼承只會繼承父類的原型數據。不單單是財富,連老爸的名氣也要獲得,不錯不錯。

ES5中的實例繼承,是先創造子類的實例對象 ,再通過 或 方法,在 上添加父類的實例屬性和方法。當然也可以選擇不繼承父類的實例數據。而ES6不同,它的設計使得實例繼承更為優秀和嚴謹。

在ES6的實例繼承中,是先調用 方法創建父類的 (依舊指向子類)和添加父類的實例數據,再通過子類的構造函數修飾 ,與ES5正好相反。ES6規定在子類的 方法里,在使用到 之前,必須先調用 方法得到子類的 。不調用 方法,意味著子類得不到 對象。


2.2 super

關鍵字 比較奇葩,在不同的環境和使用方式下,它會指代不同的東西(總的說可以指代對象或方法兩種)。而且在不顯式的指明是作為對象或方法使用時,比如 ,會直接報錯。

作為函數時。 只能存在於子類的構造方法中,這時它指代父類構造函數。

作為對象時。 在靜態方法中指代父類本身,在構造方法和原型方法中指代父類的 屬性。不過通過 調用父類方法時,方法的 依舊指向子類。即是說,通過 調用父類的靜態方法時,該方法的 指向子類本身;調用父類的原型方法時,該方法的 指向該(子類的)實例。而且通過 對某屬性賦值時,在子類的原型方法里指代該實例,在子類的靜態方法里指代子類本身,畢竟直接在子類中通過 修改父類是很危險的。

很迷糊對吧,瘋瘋癲癲的,還是結合著代碼看吧!

2.3 繼承原生構造函數

使用構造函數模式,構建繼承了原生數據結構(比如 )的子類,有許多缺陷的。一方面由上文可知,原始繼承是先創建子類 ,再通過父類構造函數進行修飾,因此無法獲取到父類的內部屬性(隱藏屬性)。另一方面,原生構造函數會直接忽略 或 方法傳入的 ,導致子類根本無法獲取到父類的實例屬性和方法。

創建類的過程,是先構造一個屬於父類卻指向子類的 (繞口),再通過父類和子類的構造函數進行修飾。因此可以規避構造函數的問題,獲取到父類的實例屬性和方法,包括內部屬性。進而真正的創建原生數據結構的子類,從而簡單的擴展原生數據類型。另外還可以通過設置 屬性,使得衍生對象為原生類而不是自定義子類的實例。

需要注意的是繼承 的子類。ES6改變了 構造函數的行為,一旦發現其不是通過 這種形式調用的,構造函數會忽略傳入的參數。由此導致 子類無法正常初始化,但這不是個大問題。


推薦

ES6精華:Symbol

ES6精華:Promise

Async:簡潔優雅的非同步之道

Generator:JS執行權的真實操作者

END

作者:wmaker

https://segmentfault.com/a/1190000016475608


喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 JavaScript 的精彩文章:

記一次涼涼的小米前端面試
Chrome 70 將繼續在地址欄隱藏網址中的 WWW

TAG:JavaScript |