javascript的六種繼承方式

  1.原型鏈

javascript的六種繼承方式

function SuperType(){erty = true;}uperValue = function(){return erty;};function SubType(){roperty = false;}//繼承了otype = new SuperType();ubValue = function (){return roperty;};var instance = new SubType();alert(uperValue()); //true

實現的本質是重寫原型物件,代之以一個新型別的例項。

  2.借用建構函式

function SuperType(){rs = ["red", "blue", "green"];}function SubType(){//繼承了(this);}var instance1 = new SubType();("black");alert(rs); //"red,blue,green,black"var instance2 = new SubType();alert(rs); //"red,blue,green"

如果僅僅是借用建構函式,那麼也將無法避免建構函式模式存在的問題——方法都在建構函式中定義,因此函式複用就無從談起了。而且,在超型別的原型中定義的方法,對子型別而言也是不可見的,結果所有型別都只能使用建構函式模式。考慮到這些問題,借用建構函式的技術也是很少單獨使用的。

  3.組合繼承

function SuperType(name){ = name;rs = ["red", "blue", "green"];}ame = function(){alert();};function SubType(name, age){//繼承屬性(this, name); = age;}//繼承方法otype = new SuperType();tructor = SubType;ge = function(){alert();};var instance1 = new SubType("Nicholas", 29);("black");alert(rs); //"red,blue,green,black"ame(); //"Nicholas";ge(); //29var instance2 = new SubType("Greg", 27);alert(rs); //"red,blue,green"ame(); //"Greg";ge(); //27

在這個例子中,SuperType 建構函式定義了兩個屬性:name 和colors。SuperType 的原型定義了一個方法sayName()。SubType 建構函式在呼叫SuperType 建構函式時傳入了name 引數,緊接著又定義了它自己的屬性age。然後,將SuperType 的例項賦值給SubType 的原型,然後又在該新原型上定義了方法sayAge()。這樣一來,就可以讓兩個不同的SubType 例項既分別擁有自己屬性——包括colors 屬性,又可以使用相同的方法了。

組合繼承避免了原型鏈和借用建構函式的缺陷,融合了它們的優點,成為JavaScript 中最常用的繼承模式。而且,instanceof 和isPrototypeOf()也能夠用於識別基於組合繼承建立的物件。

  4.原型式繼承

function object(o){function F(){}otype = o;return new F();}

在object()函式內部,先建立了一個臨時性的建構函式,然後將傳入的物件作為這個建構函式的原型,最後返回了這個臨時型別的一個新例項。從本質上講,object()對傳入其中的物件執行了一次淺複製。來看下面的例子。

var person = {name: "Nicholas",friends: ["Shelby", "Court", "Van"]};var anotherPerson = object(person); = "Greg";("Rob");var yetAnotherPerson = object(person); = "Linda";("Barbie");alert(nds); //"Shelby,Court,Van,Rob,Barbie"

克羅克福德主張的這種原型式繼承,要求你必須有一個物件可以作為另一個物件的基礎。如果有這麼一個物件的話,可以把它傳遞給object()函式,然後再根據具體需求對得到的物件加以修改即可。在這個例子中,可以作為另一個物件基礎的是person 物件,於是我們把它傳入到object()函式中,然後該函式就會返回一個新物件。這個新物件將person 作為原型,所以它的原型中就包含一個基本型別值屬性和一個引用型別值屬性。這意味著nds 不僅屬於person 所有,而且也會被anotherPerson以及yetAnotherPerson 共享。實際上,這就相當於又建立了person 物件的兩個副本。

ECMAScript 5 通過新增te()方法規範化了原型式繼承。這個方法接收兩個引數:一個用作新物件原型的物件和(可選的)一個為新物件定義額外屬性的物件。在傳入一個引數的情況下,te()與object()方法的行為相同。

var person = {name: "Nicholas",friends: ["Shelby", "Court", "Van"]};var anotherPerson = te(person); = "Greg";("Rob");var yetAnotherPerson = te(person); = "Linda";("Barbie");alert(nds); //"Shelby,Court,Van,Rob,Barbie"

te()方法的第二個引數與neProperties()方法的第二個引數格式相同:每個屬性都是通過自己的`描述符定義的。以這種方式指定的任何屬性都會覆蓋原型物件上的同名屬性。例如:

var person = {name: "Nicholas",friends: ["Shelby", "Court", "Van"]};var anotherPerson = te(person, {name: {value: "Greg"}});alert(); //"Greg"

支援te()方法的瀏覽器有IE9+、Firefox 4+、Safari 5+、Opera 12+和Chrome。

在沒有必要興師動眾地建立建構函式,而只想讓一個物件與另一個物件保持類似的情況下,原型式繼承是完全可以勝任的。不過別忘了,包含引用型別值的屬性始終都會共享相應的值,就像使用原型模式一樣。

  5.寄生式繼承

寄生式(parasitic)繼承是與原型式繼承緊密相關的一種思路,並且同樣也是由克羅克福德推而廣之的。寄生式繼承的思路與寄生建構函式和工廠模式類似,即建立一個僅用於封裝繼承過程的函式,該函式在內部以某種方式來增強物件,最後再像真地是它做了所有工作一樣返回物件。以下程式碼示範了寄生式繼承模式。

function createAnother(original){var clone = object(original); //通過呼叫函式建立一個新物件i = function(){ //以某種方式來增強這個物件alert("hi");};return clone; //返回這個物件}

在這個例子中,createAnother()函式接收了一個引數,也就是將要作為新物件基礎的物件。然後,把這個物件(original)傳遞給object()函式,將返回的結果賦值給clone。再為clone 物件新增一個新方法sayHi(),最後返回clone 物件。可以像下面這樣來使用createAnother()函式:

var person = {name: "Nicholas",friends: ["Shelby", "Court", "Van"]};var anotherPerson = createAnother(person);i(); //"hi"

這個例子中的程式碼基於person 返回了一個新物件——anotherPerson。新物件不僅具有person的所有屬性和方法,而且還有自己的sayHi()方法。

在主要考慮物件而不是自定義型別和建構函式的情況下,寄生式繼承也是一種有用的模式。前面示範繼承模式時使用的object()函式不是必需的;任何能夠返回新物件的函式都適用於此模式。

使用寄生式繼承來為物件新增函式,會由於不能做到函式複用而降低效率;這一

點與建構函式模式類似。

  6.寄生組合式繼承

前面說過,組合繼承是JavaScript 最常用的繼承模式;不過,它也有自己的不足。組合繼承最大的問題就是無論什麼情況下,都會呼叫兩次超型別建構函式:一次是在建立子型別原型的時候,另一次是在子型別建構函式內部。沒錯,子型別最終會包含超型別物件的全部例項屬性,但我們不得不在呼叫子型別建構函式時重寫這些屬性。再來看一看下面組合繼承的例子。

function SuperType(name){ = name;rs = ["red", "blue", "green"];}ame = function(){alert();};function SubType(name, age){(this, name); //第二次呼叫SuperType() = age;}otype = new SuperType(); //第一次呼叫SuperType()tructor = SubType;ge = function(){alert();};

加粗字型的行中是呼叫SuperType 建構函式的程式碼。在第一次呼叫SuperType 建構函式時,otype 會得到兩個屬性:name 和colors;它們都是SuperType 的例項屬性,只不過現在位於SubType 的原型中。當呼叫SubType 建構函式時,又會呼叫一次SuperType 建構函式,這一次又在新物件上建立了例項屬性name 和colors。於是,這兩個屬性就遮蔽了原型中的兩個同名屬性。圖6-6 展示了上述過程。

如圖6-6 所示,有兩組name 和colors 屬性:一組在例項上,一組在SubType 原型中。這就是呼叫兩次SuperType 建構函式的結果。好在我們已經找到了解決這個問題方法——寄生組合式繼承。

所謂寄生組合式繼承,即通過借用建構函式來繼承屬性,通過原型鏈的混成形式來繼承方法。其背後的基本思路是:不必為了指定子型別的原型而呼叫超型別的建構函式,我們所需要的無非就是超型別原型的一個副本而已。本質上,就是使用寄生式繼承來繼承超型別的原型,然後再將結果指定給子型別的原型。寄生組合式繼承的基本模式如下所示。

function inheritPrototype(subType, superType){var prototype = object(otype); //建立物件tructor = subType; //增強物件otype = prototype; //指定物件}

這個示例中的inheritPrototype()函式實現了寄生組合式繼承的最簡單形式。這個函式接收兩個引數:子型別建構函式和超型別建構函式。在函式內部,第一步是建立超型別原型的一個副本。第二步是為建立的副本新增constructor 屬性,從而彌補因重寫原型而失去的預設的constructor 屬性。最後一步,將新建立的物件(即副本)賦值給子型別的原型。這樣,我們就可以用呼叫inherit-Prototype()函式的語句,去替換前面例子中為子型別原型賦值的語句了,例如

function SuperType(name){ = name;rs = ["red", "blue", "green"];}ame = function(){alert();};function SubType(name, age){(this, name); = age;}inheritPrototype(SubType, SuperType);ge = function(){alert();};

這個例子的高效率體現在它只調用了一次SuperType 建構函式,並且因此避免了在otype 上面建立不必要的、多餘的屬性。與此同時,原型鏈還能保持不變;因此,還能夠正常使用instanceof 和isPrototypeOf()。開發人員普遍認為寄生組合式繼承是引用型別最理想的繼承正規化。

YUI 的nd()方法採用了寄生組合繼承,從而讓這種模式首次出現在了一個應用非常廣泛的JavaScript 庫中。要了解有關YUI 的更多資訊,請訪問http://developer. 。

以上所述就是本文的全部內容了,希望對大家學習javascript繼承有所幫助。