繼承是面嚮對象語言中一個最爲人津津樂道的概念。許多面向對象語言都支持兩種繼承方式:接口繼承和實現繼承。接口繼承只繼承方法簽名,而實現繼承則繼承實際的方法。在ECMAScript中,函數沒有簽名,沒法實現接口繼承,只支持實現繼承,繼承主要依靠的是原型鏈。函數
先來看一個簡單的原型鏈繼承示例,以下圖:this
首先聲明一個Human
函數,給它的原型添加一個getHumanSex
方法。而後聲明一個Person
函數,要讓Person
繼承Human
,能使用Human
的屬性和方法,只要把Person
的原型從新賦值爲Human
的一個實例就行了。最後給Person
的原型(也就是Human
的這個實例)添加一個getPersonSex
方法。如今建立Person
的一個實例personObj
,name = '張三',age = 12
。若是訪問personObj.name
,那麼就能夠直接訪問到是張三
了。假如如今要訪問humanSex
,由於personOb
j中沒有,因此會順着__proto__
屬性到Person
的原型中去找,而Person
的原型就是Human
的一個實例,它的humanSex
屬性就是'男/女'
,因此personObj.humanSex
的值就是'男/女'
,這就達到了繼承的效果。假如要訪問getPersonSex
,personObj
中沒有,也是到原型中去找,找到了就調用,由於是使用personObj.getPersonSex
,因此在方法getPersonSex
中,this
指的就是personObj
,return this.personSex
就是返回personObj.personSex
,即爲'男'
。 若是是調用personObj.getHumanSex
,仍是先順着__proto__
到Person
的原型中去找,Person
的原型是Human
的一個實例,裏面沒有,那怎麼辦呢,由於該原型是Human
的一個實例,因此它天然也會有個__proto__
指向Human
的原型,因而就繼續順着這個__proto__
到Human
的原型中去找,這就是所謂的原型鏈,找到了就開始調用,天然getHumanSex
中的this
指的也就是personObj
,return this.humanSex
就是return personObj.humanSex
,即'男/女'
。這就是原型鏈繼承。.net
細心的讀者也許會發現,在Human
的原型中也有個__proto__
,那麼Human
的原型會不會也是某個函數的一個實例呢?若是咱們調用personObj.toString()
,那麼咱們調用的到底是誰的toString()
呢?要回答這個問題,咱們來看下面這幅示意圖:code
Human
的原型實際上是Object
的一個實例,天然__proto__
指向的就是Object
的原型,而toString
就是Object
原型上的方法。對象
其實ECMAScript中全部引用類型的默認原型都是Object
的實例,也就是說全部引用類型默認都繼承了Object
,而這個繼承正是經過原型鏈實現的。blog
原型的好處是全部的實例能共享它所包含的屬性和方法,也就是說沒必要在構造函數中定義對象實例的信息,但正如JavaScript建立對象(三)——原型模式中所說,這也帶來了一個問題。原型屬性會被全部實例共享,對於引用類型會出現一些應用時的共享問題,因此須要在構造函中,而不是在原型對象中定義屬性。可是在經過原型來實現繼承時,原型實際上就是另外一個函數的實例。因而,原來的實例屬性也就瓜熟蒂落地變成了如今的原型屬性了,那麼原型共享數據的問題又出現了。繼承
另一個問題是,建立子類型的實例時,無法向超類型的構造函數中傳遞參數。實際上,應該說是沒辦法在不影響全部對象的實例下,給超類型的構造函數傳遞參數。有鑑於此,實踐中不多會單獨使用原型鏈。接口