JavaScript繼承(一)——原型鏈

繼承是面嚮對象語言中一個最爲人津津樂道的概念。許多面向對象語言都支持兩種繼承方式:接口繼承和實現繼承。接口繼承只繼承方法簽名,而實現繼承則繼承實際的方法。在ECMAScript中,函數沒有簽名,沒法實現接口繼承,只支持實現繼承,繼承主要依靠的是原型鏈。函數

先來看一個簡單的原型鏈繼承示例,以下圖:this

首先聲明一個Human函數,給它的原型添加一個getHumanSex方法。而後聲明一個Person函數,要讓Person繼承Human,能使用Human的屬性和方法,只要把Person的原型從新賦值爲Human的一個實例就行了。最後給Person的原型(也就是Human的這個實例)添加一個getPersonSex方法。如今建立Person的一個實例personObjname = '張三',age = 12。若是訪問personObj.name,那麼就能夠直接訪問到是張三了。假如如今要訪問humanSex,由於personObj中沒有,因此會順着__proto__屬性到Person的原型中去找,而Person的原型就是Human的一個實例,它的humanSex屬性就是'男/女',因此personObj.humanSex的值就是'男/女',這就達到了繼承的效果。假如要訪問getPersonSexpersonObj中沒有,也是到原型中去找,找到了就調用,由於是使用personObj.getPersonSex,因此在方法getPersonSex中,this指的就是personObjreturn this.personSex就是返回personObj.personSex,即爲'男'。 若是是調用personObj.getHumanSex,仍是先順着__proto__Person的原型中去找,Person的原型是Human的一個實例,裏面沒有,那怎麼辦呢,由於該原型是Human的一個實例,因此它天然也會有個__proto__指向Human的原型,因而就繼續順着這個__proto__Human的原型中去找,這就是所謂的原型鏈,找到了就開始調用,天然getHumanSex中的this指的也就是personObjreturn this.humanSex就是return personObj.humanSex,即'男/女'。這就是原型鏈繼承。.net

細心的讀者也許會發現,在Human的原型中也有個__proto__,那麼Human的原型會不會也是某個函數的一個實例呢?若是咱們調用personObj.toString(),那麼咱們調用的到底是誰的toString()呢?要回答這個問題,咱們來看下面這幅示意圖:code

Human的原型實際上是Object的一個實例,天然__proto__指向的就是Object的原型,而toString就是Object原型上的方法。對象

其實ECMAScript中全部引用類型的默認原型都是Object的實例,也就是說全部引用類型默認都繼承了Object,而這個繼承正是經過原型鏈實現的。blog

原型的好處是全部的實例能共享它所包含的屬性和方法,也就是說沒必要在構造函數中定義對象實例的信息,但正如JavaScript建立對象(三)——原型模式中所說,這也帶來了一個問題。原型屬性會被全部實例共享,對於引用類型會出現一些應用時的共享問題,因此須要在構造函中,而不是在原型對象中定義屬性。可是在經過原型來實現繼承時,原型實際上就是另外一個函數的實例。因而,原來的實例屬性也就瓜熟蒂落地變成了如今的原型屬性了,那麼原型共享數據的問題又出現了。繼承

另一個問題是,建立子類型的實例時,無法向超類型的構造函數中傳遞參數。實際上,應該說是沒辦法在不影響全部對象的實例下,給超類型的構造函數傳遞參數。有鑑於此,實踐中不多會單獨使用原型鏈。接口

相關文章
相關標籤/搜索