繼承是OO語言中一個重要的特性和概念。許多的OO語言中都支持兩種繼承方式:接口繼承和實現繼承。
ECMAScript只支持實現繼承,其實現繼承主要是靠原型鏈來實現。在PHP語言中,是使用extend來實現繼承。那么我們就先來講講原型鏈。
原型鏈的基本思想是利用原型讓一個引用類型繼承另一個引用類型的屬性和方法。
簡單回顧下構造函數、原型和實例的關系:
每個構造函數都有一個原型對象,原型對象包含一個指向構造函數的指針(prototype),而實例則包含一個指向原型對象的內部指針(__proto__)。
實現原型鏈有一種基本模式,其代碼大致如下:
function SuperType() { this.property = true; } SuperType.prototype.getSuperValue = function() { return this.property; }; function SubType() { this.subproperty = false; } //繼承了SuperType SubType.prototype = new SuperType(); SubType.prototype.getSubValue = function() { return this.subproperty; }; var instance = new SubType(); alert(instance.getSubValue()); //true
以上代碼是定義了2個類型:SuperType()和SubType(),每個類型分別有一個屬性和方法。2者的區別是SubType繼承了SuperType。通過創建SuperType實例並將該實例賦給SubType.prototype。原來存在於SuperType中所有的屬性和方法也都存在於SubType.prototype中了。在確立了這種繼承關系后,又在SubType.prototype中添加了一個方法。
請看下圖展示的實例以及構造函數和原型之間的關系:PS:找線畫了半天沒畫好,先這么看吧:)
確定原型和實例的關系:
可以通過兩種方式來確定原型和實例之間的關系。第一種方式是使用instanceof操作符;第二種是使用isPrototypeOf()方法。
alert(instance instanceof Object); //true alert(instance instanceof SuperType); //true alert(instance instanceof SubType); //true alert(Object.prototype.isPrototypeOf(instance)); //true alert(SuperType.prototype.isPrototypeOf(instance)); //true alert(SubType.prototype.isPrototypeOf(instance)); //true
原型鏈的問題:
雖然很強大,可以用它來實現繼承,但它也存在一些問題。最主要的問題來自包含引用類型值的原型。在通過原型來實現繼承時,原型實際上會變成一個類型的實例。於是,原先的實例屬性也順理成章的變成了現在的原型的屬性了。看例子吧!
function SuperType() { this.colors = ["red", "green", "blue"]; } function SubType() { } //繼承了SuperType SubType.prototype = new SuperType(); var instance1 = new SubType(); instance1.colors.push("black"); alert(instance1.colors); //red,green,blue,black var instance2 = new SubType(); alert(instance2.colors); //red,green,blue,black
SuperType構造函數定義了一個屬性,該屬性包含一個數組(引用類型值)。當SubType通過原型繼承了SuperType之后,SubType.prototype就變成了SuperType的一個實例。因此也擁有了SuperType的所有屬性和方法。結果SubType會共享這個colors屬性,我們通過對instance1.colors的修改,能夠通過instance2.colors反映出來。
第二個問題是創建子類型的實例時,不能向超類型的構造函數中傳遞函數。鑒於這幾點問題,實踐中很少會單獨使用原型鏈。