- 相關(guān)推薦
javascript的六種繼承方式
1.原型鏈
function SuperType(){this.property = true;}SuperType.prototype.getSuperValue = function(){return this.property;};function SubType(){this.subproperty = false;}//繼承了SuperTypeSubType.prototype = new SuperType();SubType.prototype.getSubValue = function (){return this.subproperty;};var instance = new SubType();alert(instance.getSuperValue()); //true
實(shí)現的本質(zhì)是重寫(xiě)原型對象,代之以一個(gè)新類(lèi)型的實(shí)例。
2.借用構造函數
function SuperType(){this.colors = ["red", "blue", "green"];}function SubType(){//繼承了SuperTypeSuperType.call(this);}var instance1 = new SubType();instance1.colors.push("black");alert(instance1.colors); //"red,blue,green,black"var instance2 = new SubType();alert(instance2.colors); //"red,blue,green"
如果僅僅是借用構造函數,那么也將無(wú)法避免構造函數模式存在的問(wèn)題——方法都在構造函數中定義,因此函數復用就無(wú)從談起了。而且,在超類(lèi)型的原型中定義的方法,對子類(lèi)型而言也是不可見(jiàn)的,結果所有類(lèi)型都只能使用構造函數模式?紤]到這些問(wèn)題,借用構造函數的技術(shù)也是很少單獨使用的。
3.組合繼承
function SuperType(name){this.name = name;this.colors = ["red", "blue", "green"];}SuperType.prototype.sayName = function(){alert(this.name);};function SubType(name, age){//繼承屬性SuperType.call(this, name);this.age = age;}//繼承方法SubType.prototype = new SuperType();SubType.prototype.constructor = SubType;SubType.prototype.sayAge = function(){alert(this.age);};var instance1 = new SubType("Nicholas", 29);instance1.colors.push("black");alert(instance1.colors); //"red,blue,green,black"instance1.sayName(); //"Nicholas";instance1.sayAge(); //29var instance2 = new SubType("Greg", 27);alert(instance2.colors); //"red,blue,green"instance2.sayName(); //"Greg";instance2.sayAge(); //27
在這個(gè)例子中,SuperType 構造函數定義了兩個(gè)屬性:name 和colors。SuperType 的原型定義了一個(gè)方法sayName()。SubType 構造函數在調用SuperType 構造函數時(shí)傳入了name 參數,緊接著(zhù)又定義了它自己的屬性age。然后,將SuperType 的實(shí)例賦值給SubType 的原型,然后又在該新原型上定義了方法sayAge()。這樣一來(lái),就可以讓兩個(gè)不同的SubType 實(shí)例既分別擁有自己屬性——包括colors 屬性,又可以使用相同的方法了。
組合繼承避免了原型鏈和借用構造函數的缺陷,融合了它們的優(yōu)點(diǎn),成為JavaScript 中最常用的繼承模式。而且,instanceof 和isPrototypeOf()也能夠用于識別基于組合繼承創(chuàng )建的對象。
4.原型式繼承
function object(o){function F(){}F.prototype = o;return new F();}
在object()函數內部,先創(chuàng )建了一個(gè)臨時(shí)性的構造函數,然后將傳入的對象作為這個(gè)構造函數的原型,最后返回了這個(gè)臨時(shí)類(lèi)型的一個(gè)新實(shí)例。從本質(zhì)上講,object()對傳入其中的對象執行了一次淺復制。來(lái)看下面的例子。
var person = {name: "Nicholas",friends: ["Shelby", "Court", "Van"]};var anotherPerson = object(person);anotherPerson.name = "Greg";anotherPerson.friends.push("Rob");var yetAnotherPerson = object(person);yetAnotherPerson.name = "Linda";yetAnotherPerson.friends.push("Barbie");alert(person.friends); //"Shelby,Court,Van,Rob,Barbie"
克羅克福德主張的這種原型式繼承,要求你必須有一個(gè)對象可以作為另一個(gè)對象的基礎。如果有這么一個(gè)對象的話(huà),可以把它傳遞給object()函數,然后再根據具體需求對得到的對象加以修改即可。在這個(gè)例子中,可以作為另一個(gè)對象基礎的是person 對象,于是我們把它傳入到object()函數中,然后該函數就會(huì )返回一個(gè)新對象。這個(gè)新對象將person 作為原型,所以它的原型中就包含一個(gè)基本類(lèi)型值屬性和一個(gè)引用類(lèi)型值屬性。這意味著(zhù)person.friends 不僅屬于person 所有,而且也會(huì )被anotherPerson以及yetAnotherPerson 共享。實(shí)際上,這就相當于又創(chuàng )建了person 對象的兩個(gè)副本。
ECMAScript 5 通過(guò)新增Object.create()方法規范化了原型式繼承。這個(gè)方法接收兩個(gè)參數:一個(gè)用作新對象原型的對象和(可選的)一個(gè)為新對象定義額外屬性的對象。在傳入一個(gè)參數的情況下,Object.create()與object()方法的行為相同。
var person = {name: "Nicholas",friends: ["Shelby", "Court", "Van"]};var anotherPerson = Object.create(person);anotherPerson.name = "Greg";anotherPerson.friends.push("Rob");var yetAnotherPerson = Object.create(person);yetAnotherPerson.name = "Linda";yetAnotherPerson.friends.push("Barbie");alert(person.friends); //"Shelby,Court,Van,Rob,Barbie"
Object.create()方法的第二個(gè)參數與Object.defineProperties()方法的第二個(gè)參數格式相同:每個(gè)屬性都是通過(guò)自己的描述符定義的。以這種方式指定的任何屬性都會(huì )覆蓋原型對象上的同名屬性。例如:
var person = {name: "Nicholas",friends: ["Shelby", "Court", "Van"]};var anotherPerson = Object.create(person, {name: {value: "Greg"}});alert(anotherPerson.name); //"Greg"
支持Object.create()方法的瀏覽器有IE9+、Firefox 4+、Safari 5+、Opera 12+和Chrome。
在沒(méi)有必要興師動(dòng)眾地創(chuàng )建構造函數,而只想讓一個(gè)對象與另一個(gè)對象保持類(lèi)似的情況下,原型式繼承是完全可以勝任的。不過(guò)別忘了,包含引用類(lèi)型值的屬性始終都會(huì )共享相應的值,就像使用原型模式一樣。
5.寄生式繼承
寄生式(parasitic)繼承是與原型式繼承緊密相關(guān)的一種思路,并且同樣也是由克羅克福德推而廣之的。寄生式繼承的思路與寄生構造函數和工廠(chǎng)模式類(lèi)似,即創(chuàng )建一個(gè)僅用于封裝繼承過(guò)程的函數,該函數在內部以某種方式來(lái)增強對象,最后再像真地是它做了所有工作一樣返回對象。以下代碼示范了寄生式繼承模式。
function createAnother(original){var clone = object(original); //通過(guò)調用函數創(chuàng )建一個(gè)新對象clone.sayHi = function(){ //以某種方式來(lái)增強這個(gè)對象alert("hi");};return clone; //返回這個(gè)對象}
在這個(gè)例子中,createAnother()函數接收了一個(gè)參數,也就是將要作為新對象基礎的對象。然后,把這個(gè)對象(original)傳遞給object()函數,將返回的結果賦值給clone。再為clone 對象添加一個(gè)新方法sayHi(),最后返回clone 對象?梢韵裣旅孢@樣來(lái)使用createAnother()函數:
var person = {name: "Nicholas",friends: ["Shelby", "Court", "Van"]};var anotherPerson = createAnother(person);anotherPerson.sayHi(); //"hi"
這個(gè)例子中的代碼基于person 返回了一個(gè)新對象——anotherPerson。新對象不僅具有person的所有屬性和方法,而且還有自己的sayHi()方法。
在主要考慮對象而不是自定義類(lèi)型和構造函數的情況下,寄生式繼承也是一種有用的模式。前面示范繼承模式時(shí)使用的object()函數不是必需的;任何能夠返回新對象的函數都適用于此模式。
使用寄生式繼承來(lái)為對象添加函數,會(huì )由于不能做到函數復用而降低效率;這一
點(diǎn)與構造函數模式類(lèi)似。
6.寄生組合式繼承
前面說(shuō)過(guò),組合繼承是JavaScript 最常用的繼承模式;不過(guò),它也有自己的不足。組合繼承最大的問(wèn)題就是無(wú)論什么情況下,都會(huì )調用兩次超類(lèi)型構造函數:一次是在創(chuàng )建子類(lèi)型原型的時(shí)候,另一次是在子類(lèi)型構造函數內部。沒(méi)錯,子類(lèi)型最終會(huì )包含超類(lèi)型對象的全部實(shí)例屬性,但我們不得不在調用子類(lèi)型構造函數時(shí)重寫(xiě)這些屬性。再來(lái)看一看下面組合繼承的例子。
function SuperType(name){this.name = name;this.colors = ["red", "blue", "green"];}SuperType.prototype.sayName = function(){alert(this.name);};function SubType(name, age){SuperType.call(this, name); //第二次調用SuperType()this.age = age;}SubType.prototype = new SuperType(); //第一次調用SuperType()SubType.prototype.constructor = SubType;SubType.prototype.sayAge = function(){alert(this.age);};
加粗字體的行中是調用SuperType 構造函數的代碼。在第一次調用SuperType 構造函數時(shí),SubType.prototype 會(huì )得到兩個(gè)屬性:name 和colors;它們都是SuperType 的實(shí)例屬性,只不過(guò)現在位于SubType 的原型中。當調用SubType 構造函數時(shí),又會(huì )調用一次SuperType 構造函數,這一次又在新對象上創(chuàng )建了實(shí)例屬性name 和colors。于是,這兩個(gè)屬性就屏蔽了原型中的兩個(gè)同名屬性。圖6-6 展示了上述過(guò)程。
如圖6-6 所示,有兩組name 和colors 屬性:一組在實(shí)例上,一組在SubType 原型中。這就是調用兩次SuperType 構造函數的結果。好在我們已經(jīng)找到了解決這個(gè)問(wèn)題方法——寄生組合式繼承。
所謂寄生組合式繼承,即通過(guò)借用構造函數來(lái)繼承屬性,通過(guò)原型鏈的混成形式來(lái)繼承方法。其背后的基本思路是:不必為了指定子類(lèi)型的原型而調用超類(lèi)型的構造函數,我們所需要的無(wú)非就是超類(lèi)型原型的一個(gè)副本而已。本質(zhì)上,就是使用寄生式繼承來(lái)繼承超類(lèi)型的原型,然后再將結果指定給子類(lèi)型的原型。寄生組合式繼承的基本模式如下所示。
function inheritPrototype(subType, superType){var prototype = object(superType.prototype); //創(chuàng )建對象prototype.constructor = subType; //增強對象subType.prototype = prototype; //指定對象}
這個(gè)示例中的inheritPrototype()函數實(shí)現了寄生組合式繼承的最簡(jiǎn)單形式。這個(gè)函數接收兩個(gè)參數:子類(lèi)型構造函數和超類(lèi)型構造函數。在函數內部,第一步是創(chuàng )建超類(lèi)型原型的一個(gè)副本。第二步是為創(chuàng )建的副本添加constructor 屬性,從而彌補因重寫(xiě)原型而失去的默認的constructor 屬性。最后一步,將新創(chuàng )建的對象(即副本)賦值給子類(lèi)型的原型。這樣,我們就可以用調用inherit-Prototype()函數的語(yǔ)句,去替換前面例子中為子類(lèi)型原型賦值的語(yǔ)句了,例如
function SuperType(name){this.name = name;this.colors = ["red", "blue", "green"];}SuperType.prototype.sayName = function(){alert(this.name);};function SubType(name, age){SuperType.call(this, name);this.age = age;}inheritPrototype(SubType, SuperType);SubType.prototype.sayAge = function(){alert(this.age);};
這個(gè)例子的高效率體現在它只調用了一次SuperType 構造函數,并且因此避免了在SubType.prototype 上面創(chuàng )建不必要的、多余的屬性。與此同時(shí),原型鏈還能保持不變;因此,還能夠正常使用instanceof 和isPrototypeOf()。開(kāi)發(fā)人員普遍認為寄生組合式繼承是引用類(lèi)型最理想的繼承范式。
YUI 的YAHOO.lang.extend()方法采用了寄生組合繼承,從而讓這種模式首次出現在了一個(gè)應用非常廣泛的JavaScript 庫中。要了解有關(guān)YUI 的更多信息,請訪(fǎng)問(wèn)http://developer. yahoo.com/yui/。
以上所述就是本文的全部?jì)热萘,希望對大家學(xué)習javascript繼承有所幫助。
【javascript的六種繼承方式】相關(guān)文章:
適合自己健身運動(dòng)的六種方式12-21
優(yōu)秀領(lǐng)導者的六種領(lǐng)導方式08-08
最容易吃罰單的六種停車(chē)和開(kāi)車(chē)方式10-28
對javascript的理解08-08
常用的JavaScript模式09-22
Javascript的this用法簡(jiǎn)述08-15
JavaScript學(xué)習筆記08-24
JavaScript 基礎教學(xué)09-29
JavaScript的課堂講解09-03
JavaScript常用方法匯總10-25