JavaScript 繼承模式-引出終極版

上一講中我們瞭解下JavaScript原型及原型鏈,這對寫成繼承模式的JavaScript代碼十分重要,下面我會講解幾種模式,最後引出最重要的模式。

原型鏈繼承

其實上一講中,我們可以實現簡單的繼承,在構成原型鏈的過程中,本身就實現了繼承

JavaScript 繼承模式-引出終極版

但是我們會發現,我們會將父類所有的屬性,方法都繼承過來,不管有用沒用,所以這種繼承方式一般不用,浪費資源。

借用構造函數

聽名字就知道,這是借用,不用自己生產,在講借用構造函數模式之前先講一下call,之前我們就說過這個call可以改變this的指向,正是因為改變this指向,所以就達到調用別處屬性,方法的目的,就這麼陰錯陽差的達到了我們的繼承目的,舉例說明:

JavaScript 繼承模式-引出終極版

輸出:

sunqiang

Person.call(this);這裡的this原本指向的是Son實例,但通過Person.call後,便將this的指向改成指向Person實例了,正因為這樣在Son沒有聲明任何屬性,方法的前提下,就能訪問所謂父級Person的屬性,方法,這樣一來就達成繼承的目的了。

先撇開繼承,單單這一特點就非常有用了,像上一講中提到的Object.prototype.toStirng.call();便可以將對象執行最原始的toString()方法,實現查明此對象類型的目的。

針對繼承這一個話題,用call實現的借用構造函數的方式也不太理想,最直接的問題就是,無法繼承該借用的構造函數的原型方法,像上面的例子裡,如果我們給Peson原型添加屬性或方法:Person.prototype.getName(),getName()方法實例son是訪問不到的;還有一個問題就是在Son構造函數里面雖然代碼少了,但是執行的時候還是要執行Person函數的,不實例化Person函數,裡面的this指向就沒有用的。

所以基於以上兩點,尤其是第一點,借用構造函數實現的繼承也不理想。

共享模式繼承

共享就是說當前構造函數原型直接指向需要繼承的構造函數的原型,如下圖:

JavaScript 繼承模式-引出終極版

這樣實例son就擁有Person原型的所有屬性,方法,我們可以創建許多類似這樣的構造函數,讓它們的原型指向Person原型,共享一個原型。

但這裡有一個問題,當我們給Son的原型添加屬性和方法時,凡是屬性._proto_指向Person.prototype的實例也擁有了這些屬性和方法。那怎麼解決這個問題呢?還有一種模式,寄生繼承,也叫聖盃模式。

寄生繼承

我們發現上面的繼承有共享的問題,改變一處,處處改變,這顯然不是我們想要的,我回過頭看看之前的原型鏈模式,它為什麼沒有這樣的問題呢?Son.prototype=new Person();我們會發現這裡的Son.prototype指向的是new person()的一個實例化對象,發生了new就等於重新開闢空間,this是全新的值,我們仿照這樣的方法,改善一下共享模式。

JavaScript 繼承模式-引出終極版

這樣就不會出現共享模式下出現的問題了,為了讓代碼顯得工業化些,稍做修改

JavaScript 繼承模式-引出終極版

其中的Target.prototype.constructor指向的構造函數,意思是說某個實例是由哪個函數構造的;至於為什麼將var F=function(){}放在放到返回函數的外面,而且返回的函數里面還能訪問到,我會在今後的閉包中有所介紹。

細心的夥伴也學會發現,你上面的模式也有個問題,你子類實例訪問不到父類構造函數里面的屬性,確實是這樣的,解決這個這個問題的方法就是在子類的構造函數里面寫上call,結合借用構造函數的方法來實現最終的繼承。

JavaScript 繼承模式-引出終極版

至此,關於繼承的話題講解完了,繼承的幾種模式就是:原型鏈模式,借用構造函數模式,寄生模式(寄生借用模式)。網上還有一些其它的模式,我認為其實完全沒有必要了解那麼多,最後用的就是寄生借用模式,之所以我會講前面的幾種,完全是為講最後一種模式服務的,清楚這種模式是怎麼來的,然後怎麼去用就夠了。
















分享到:


相關文章: