天天看點

【javascript基礎】7、繼承前言繼承小結

由于本人水準有限,是以有些高手覺得現在寫的内容偏容易,要一點點來嘛,今天和大家學習或者複習一下javascript的繼承。我也就是盡量寫吧······

javascript的繼承其實主要就是通過原型鍊來實作的,原型鍊我們之前已經和大家一起學習過,這裡就不浪費大家的時間了。javascript連類都沒有,還說啥繼承呢,這還是模拟類的繼承。《javascript進階程式設計》上分成了幾個方式,有的書上分為類式繼承,原型式繼承,這就是模拟其他語言類的繼承,還有什麼用摻元類實作的,在這裡都和大家說下。

在這裡在說一下原型鍊的概念,因為javascript的繼承都是通過原型鍊來模拟的,是以在這裡幫助大家了解一下。我們知道,每一個構造函數都有一個原型對象,這個原型對象中包含一個指向構造函數的指針,同時每一個執行個體都有一個指向原型對象的内部指針。好好想一下這個關系,當我們通路一個執行個體的屬性時,現在執行個體中查找,沒找到通過内部指針去原型中查找,還是沒有再通過原型的内部指針查找原型的原型對象,一直疊代下去。嗯,就是這樣,

【javascript基礎】7、繼承前言繼承小結

現在我們知道了原型鍊是這樣的話,我們想要繼承的實作,我們要把父類的屬性和方法放在子類的原型對象中就可以了,這樣new出來的執行個體就會查找原型中的屬性和方法了,那這樣就可以實作繼承了,那我們要怎樣将父類的屬性和方法放在子類的原型中呢?我們重寫子類的原型對象是不是就可以了,這裡有個選擇的問題,我們可以讓子類的原型對象指向父類的原型對象,也可以指向一個父類的執行個體,假如現在我們将它指向了父類的原型對象,我們知道父類構造函數中的屬性就不會在子類中得到繼承,看個例子就知道了

其實這是另外一種方式的雛形,寄生組合模式的雛形,下文我會講到,這裡暫且放過。

我們現在再看看指向一個執行個體對象的情況

這下子大家會明白了,執行個體是把構造函數中this的屬性和原型中的屬性結合起來了,如果指向原型對象那麼構造函數中的屬性就不會被繼承。

【javascript基礎】7、繼承前言繼承小結

這就是繼承的最基礎和最核心的東西,這還不完善,重新原型對象我們知道,要增加一個constructor屬性,這裡不添加了,不明白的看之前的原型與原型鍊的那篇文章。javascript用instanceof來判斷執行個體與原型的關系,隻要執行個體和原型鍊中出現過構造函數,就會傳回true

原型鍊的問題:其實這個和構造對象原型鍊的問題是一樣的,主要是原型對象的屬性是一個引用類型,會引起一些問題。這是因為所有的執行個體共用原型對象的屬性,當屬性為引用類型時,任何一個執行個體對這個對象的修改會影響所有的執行個體。例子來了

原型鍊還有一個問題就是,不能向父類的構造函數中傳遞參數,就是這樣的我想給每一個子類起一個名字,這裡是無法辦到的,因為我給父類的名字都挂在了子類的原型上了。例如

這裡要起一個名字,所有執行個體都會影響,是以說沒有辦法在不影響所有執行個體的情況下給父類傳遞參數。

這個方式可以解決上面的問題,我們知道上面的原型鍊的方法是子類的原型對象指向了父類的執行個體,就是把所有父類的屬性都挂在了子類的原型對象上,所有就會出現所有執行個體共享同一個屬性引發的問題,那我們可以換一種思路,我們把一些父類的屬性放在子類的構造函數中,就是在子類的構造函數中的this添加屬性,這樣就不需要所有的屬性都弄到子類的原型對象上了,這樣每個子類的執行個體都會有自己的屬性和方法,不用共享原型中的屬性了。這是一個簡單的思路,我們在子類的構造函數中給this添加父類的屬性,我們想到了之前的apply和call方法,看例子

這時候你也可以給父類傳參數了,因為這些屬性都添加了子類構造函數中了,看例子

借用構造函數問題:這個又回歸到了構造函數模式上出現的問題了,我們所有的方法都是在構造函數上定義的,無法複用。

原型鍊和借用構造函數結合一起,使用原型鍊實作原型屬性和方法的繼承,使用借用構造函數實作對執行個體屬性的繼承,這樣通過在原型上定義方法實作函數的複用,又能保證每個執行個體都有自己的屬性。上例子

這是最常用的繼承方式。有些書叫這種為類式繼承,把這種通過構造函數方式來實作繼承的叫做類式繼承,上面的我們可以把Animal看成一個類,通過構造函數原型鍊之間的關系實作繼承。

這種沒有方式類的概念,也就是沒有使用構造函數來實作,就是使一個函數的原型指向一個原有的對象,通過這個函數來建立一個新的對象。上例子

這就是原型繼承,可以看出它存在不少問題,隻有在特定的情況下可以使用該方式,無法判斷類與執行個體之間的關系,共享引用類型屬性的問題等等。

如果知道了上面的知識,這個很好了解了,我們在建立對象那章的時候,就提到了寄生構造對象,所謂的寄生就是在函數的内部通過某種方式來增強對象之後,在傳回這個對象,那麼寄生式繼承也類似

這個看看就知道是怎麼回事了,在函數内部繼承一個對象之後,又增加了方法,之後傳回這個對象。

組合繼承上面我們說完了,組合繼承還有一個問題就是,任何時候會調用兩次父類的構造函數,一次是建立子類的原型的時候,另一次是在子類的構造函數内部。看看就知道了

我們分析一下這個過程:第一次調用的時候,在Cat.prototype對象上添加了name和colors屬性,添加到了子類的原型對象上,第二次調用父類的構造函數時,是将name和colors屬性添加到了子類的執行個體上,也就是說子類的原型對象和執行個體中都有了這兩個屬性,執行個體中的屬性屏蔽了原型中屬性。

我們想一下怎樣才能解決這問題呢?我們可以這樣,讓子類的原型對象直接指向父類的原型對象,就像文章開始我們說的那麼選擇的問題,我們這次使用父類的原型對象,這裡可以使用,是因為我們結合使用了借用構造模式,可以繼承父類構造函數中的屬性了,看看例子先

這樣是可以的,但是我們這裡就有問題了,我們在給子類的原型指定constructor屬性時,修改了父類的constructor屬性,

是以我們不能直接這樣指向父類的原型,要通過一種中轉,使子類的原型和父類原型指向不同的對象,就是使用原型模式繼承,建一個對象,這個對象的原型指向父類的原型,之後子類的原型對象再指向這個對象,這樣就使子類的原型和父類原型指向不同的對象。

就這樣循序漸進,我們就完成了javascript的繼承的内容。

複制繼承,顧名思義就是一個一個複制原型對象的屬性,将給定的類的原型的屬性循環複制到指定的原型中,

就是複制繼承,參元類就是通過這種方式來實作的,參元類是包含了一系列的通用方法,如果哪個類想用這些方法就适使用這種方式來繼承參元類。

就這樣循序漸進,我們就完成了javascript的繼承的内容,繼承這塊的知識初學者要多看書,《javascript進階程式設計》的繼承部分,多看幾遍,自己好好想想它們的優缺點,就知道該如何設計繼承了,自己在謝謝執行個體就會明白這些方式是大神們怎麼想出來的。

繼續閱讀