摘要:除此之外,在超類型的原型中定義的方法,對子類型而言也是不可兼得,結(jié)果所有類型都只能用構(gòu)造函數(shù)模式。創(chuàng)建對象增強對象指定對象繼承屬性這個例子的高效率體現(xiàn)在它只調(diào)用了一次構(gòu)造函數(shù)。
1、原型鏈
原型鏈的基本思想是利用原型讓一個引用類型繼承另一個引用類型的屬性和方法。
構(gòu)造函數(shù)、原型和實例的關系:每個構(gòu)造函數(shù)都有一個原型對象;原型對象都包含著一個指向構(gòu)造函數(shù)的指針;實例都包含一個指向原型對象的內(nèi)部指針。如果我們讓原型對象等于另一個類型的實例,此時的原型對象將包含一個指向另一個原型的指針,相應地另一個原型中也包含著指向另一個構(gòu)造函數(shù)的指針……層層遞進,就構(gòu)成了實例與原型的鏈條,這就是所謂原型鏈的基本概念。
實現(xiàn)原型鏈有一種基本模式,其代碼大致如下:
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.getSuperValue()); //true(1)不要忘記默認的原型 (2)確定原型和實例的關系
可以通過兩種方式來確定原型和實例之間的關系。
1)使用instanceof操作符,只要用這個操作符來測試實例與原型鏈中出現(xiàn)過的構(gòu)造函數(shù),結(jié)果就會返回true。
alert(instance instanceof Object); //true alert(instance instanceof SuperType); //true alert(instance instanceof SubType); //true
2)使用isPrototypeOf()方法。只要是原型鏈中出現(xiàn)過的原型,都可以說是該原型鏈所派生的實例的原型,因此isPrototypeOf()方法也會返回true。
alert(Object.prototype.isPrototypeOf(instance)); //true alert(SuperType.prototype.isPrototypeOf(instance)); //true alert(SubType.prototype.isPrototypeOf(instance)); //true(3)謹慎地定義方法
子類型有時候需要覆蓋超類型中的某個方法或者需要添加超類型中不存在的某個方法。但不管這樣,給原型添加方法的代碼一定要放在替換原型的語句之后。
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; }; //重寫超類型中的方法 SubType.prototype.getSuperValue = function() { return false; }; var instance = new SubType(); alert(instance.getSuperValue()); //false
在通過原型鏈實現(xiàn)繼承時,不能使用對象字面量創(chuàng)建原型方法。因為這樣做就會重寫原型鏈。會導致實例與原型鏈之間的聯(lián)系被切斷。
function SubType() { this.subproperty = false; } //繼承了SuperType SubType.prototype = new SuperType(); //使用字面量添加新方法,會導致上一行代碼無效 SubType.prototype = { getSubValue : function() { return this.subproperty; }, sonOtherMethod : function() { return false; } }; var instance = new SubType(); alert(instance.getSuperValue()); //error!(4)原型鏈的問題
1)最主要的問題來自包含引用類型值的原型,包含引用類型值的原型屬性會被所有實例共享;
2)第二個問題是:在創(chuàng)建子類型的實例時,不能向超類型的構(gòu)造函數(shù)中傳遞參數(shù);
function SuperType() { this.colors = ["red", "blue", "green"]; } function SubType(){ } //繼承了SuperType SubType.prototype = new SuperType(); var instance1 = new SubType(); instance1.colors.push("black"); alert(instance1.colors); //"red,blue,green,black" var instance2 = new SuperType(); alert(instance2.colors); //"red,blue,green,black"
如上所示,我們對instance1.colors的修改能夠通過instance2.colors反映出來。
2、借用構(gòu)造函數(shù)思想:在子類型構(gòu)造函數(shù)的內(nèi)部調(diào)用超類型構(gòu)造函數(shù)。
函數(shù)只不過是在特定環(huán)境中執(zhí)行代碼的對象,因此通過使用apply()和call()方法也可以在新創(chuàng)建的對象上執(zhí)行構(gòu)造函數(shù)。
function SuperType() { this.colors = ["red", "blue", "green"]; } function SubType(){ //繼承了SuperType, SuperType.call(this);//在子類型構(gòu)造函數(shù)的內(nèi)部調(diào)用超類型構(gòu)造函數(shù) } var instance1 = new SubType(); instance1.colors.push("black"); alert(instance1.colors); //"red,blue,green,black" var instance2 = new SuperType(); alert(instance2.colors); //"red,blue,green"
通過使用call()方法或apply()方法,我們實際上是在新創(chuàng)建的SubType實例的環(huán)境下調(diào)用了SuperType構(gòu)造函數(shù)。這樣一來,就會在新SubType對象上執(zhí)行SuperType()函數(shù)中定義的所有對象初始化代碼。結(jié)果SubType的每個實例就會具有自己的colors屬性的副本了。
(1)傳遞參數(shù)相對于原型鏈而言,借用構(gòu)造函數(shù)的一個很大優(yōu)勢在于:可以在子類型構(gòu)造函數(shù)中向超類型構(gòu)造函數(shù)傳遞參數(shù)。
function SuperType(name) { this.name = name; } function SubType() { //繼承了SuperType,同時還傳遞了參數(shù) SuperType.call(this, "Nicholas"); //實例屬性 this.age = 29; } var instance = new SubType(); alert(instance.name); //"Nicholas" alert(intance.age); //29
為了確保SuperType構(gòu)造函數(shù)不會重寫子類型的屬性,可以在調(diào)用超類型構(gòu)造函數(shù)后,再添加應該在子類型中定義的屬性。
(2)借用構(gòu)造函數(shù)的問題如果僅僅是借用構(gòu)造函數(shù),那么也就無法避免構(gòu)造函數(shù)模式存在的問題——方法都在構(gòu)造函數(shù)中定義,因此函數(shù)復用就無從談起。除此之外,在超類型的原型中定義的方法,對子類型而言也是不可兼得,結(jié)果所有類型都只能用構(gòu)造函數(shù)模式。
3、組合繼承將原型鏈和借用構(gòu)造函數(shù)的技術組合到一起。
思想:使用原型鏈實現(xiàn)對原型屬性和方法的繼承,而通過借用構(gòu)造函數(shù)來實現(xiàn)對實例屬性的繼承。
這樣,既通過在原型上定義方法實現(xiàn)了函數(shù)復用,又能保證每個實例都有它自己的屬性。
function SuperType(name) { this.name = name; this.color = ["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; //指向構(gòu)造函數(shù) 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(); //29 var instance2 = new SubType("Greg" ,27); alert(instance2.colors); //"red,blue,green" instance2.sayName(); //"Greg" instance2.sayAge(); //27
instanceof()和isPrototypeOf()也能夠用于識別基于組合繼承創(chuàng)建的對象、
4、原型式繼承借助原型可以基于已有的對象創(chuàng)建新對象,同時還不必因此創(chuàng)建自定義類型
function object(o) { function F() {} F.prototype =o; return new F(); }
在object()函數(shù)內(nèi)部,先創(chuàng)建了一個臨時性的構(gòu)造函數(shù),然后將傳入的對象作為這個構(gòu)造函數(shù)的原型,最后返回這個臨時類型的一個新實例。
var person = { name : "Nicholas", friends : ["Shelby", "Court", "Van"] }; var anotherPerson = object(perosn); anotherPerson.name = "Greg"; anotherPerson.friends.push("Rob"); var yetAbotherPerson = object(perosn); yetAbotherPerson.name = "Linda"; yetAbotherPerson.firends.push("Barbie"); alert(person.friends); //"Shelby, Court, Van, Greg, Linda"
ECMAScript5通過新增Object.creat()方法規(guī)范了原型式繼承。這個方法接收兩個參數(shù):用作新對象原型的對象和為新對象定義額外屬性的對象(可選)。在傳入一個參數(shù)的情況下,Object.create()與object()方法的行為相同。
var person = { name : "Nicholas", friends : ["Shelby", "Court", "Van"] }; var anotherPerson = Object.create(perosn); anotherPerson.name = "Greg"; anotherPerson.friends.push("Rob"); var yetAbotherPerson = Object.create(perosn); yetAbotherPerson.name = "Linda"; yetAbotherPerson.firends.push("Barbie"); alert(person.friends); //"Shelby, Court, Van, Greg, Linda"
Object.create()方法的第二個參數(shù)與Object.defineProperties()方法的第二個參數(shù)格式相同:每個屬性都通過自己的描述定義的。用這種方式指定的任何屬性都會覆蓋原型對象上的同名屬性。
var person = { name : "Nicholas", friends : ["Shelby", "Court", "Van"] }; var anotherPerson = Object.create(perosn,{ name : { value : "Grag" } }); alert(anotherPerson.name); //"Greg"5、寄生式繼承
創(chuàng)建一個僅用于封裝繼承過程的函數(shù),該函數(shù)在內(nèi)部以某種方式來增強對象,最后再像真的是它做了所有工作一樣返回對象。以下代碼示范了寄生式繼承模式。
function creatAnother(original) { var clone = object(original); //通過調(diào)用函數(shù)創(chuàng)建一個新對象 clone.sayHi = function() { //以某種方式來增強這個對象 alert("Hi"); }; return clone; //返回這個對象 }
基于person返回一個新對象——anotherPerson。新對象不僅具有Person的所有屬性和方法,還有自己的sayHi方法。
function creatAnother(original) { var clone = object(original); //通過調(diào)用函數(shù)創(chuàng)建一個新對象 clone.sayHi = function() { //以某種方式來增強這個對象 alert("hi"); }; return clone; //返回這個對象 } var person = { name : "Nicholas", friends : ["Shelby", "Court", "Van"] }; var anotherPerson = creatAnother(person); anotherPerson.sayHi(); //"hi"6、寄生組合式繼承
組合繼承最大的問題就是:無論在什么情況下,都會調(diào)用兩次構(gòu)造函數(shù),一次是在創(chuàng)建子類型原型的時候,一次是在子類型構(gòu)造函數(shù)的內(nèi)部,導致子類最終會包含超類對象的全部實例屬性,但我們不得不在調(diào)用子類型構(gòu)造函數(shù)時重寫這些屬性。
function SuperType(name) { this.name = name; this.color = ["red","blue","green"]; } SuperType.prototype.sayName = function() { alert(this.name); }; function SubType(name ,age) { //繼承屬性 SuperType.call(this.name); //第二次調(diào)用SuperType() this.age = age; } //繼承方法 SubType.prototype = new SuperType(); //第一次調(diào)用SuperType() SubType.prototype.constructor = SubType; //指向構(gòu)造函數(shù) SubType.prototype.sayAge = function() { alert(this.age); };
寄生式組合繼承,就是通過借用構(gòu)造函數(shù)來繼承屬性,通過原型鏈的混成形式來繼承方法。其思想是:不必為了指定子類型的原型而調(diào)用超類型的構(gòu)造函數(shù),我們所需的無非就是超類型原型的一個副本而已。本質(zhì)上就是,使用寄生式繼承來繼承超類型的原型,然再將結(jié)果指定給子類型的原型。其基本模式如下:
function inheritPrototype(SubType,superType) { var prototype = object(superType.prototype); //創(chuàng)建對象 prototype.constructor = subType; //增強對象 subType.prototype = prototype; //指定對象 }
這個函數(shù)接收兩個參數(shù):子類型構(gòu)造函數(shù)和超類型構(gòu)造函數(shù)。
第一步是創(chuàng)建超類型原型的一個副本;
第二步是為創(chuàng)建的副本添加constructor屬性,從而彌補因重寫原型而失去的默認的constructor屬性;
第三步將新創(chuàng)建的對象(即副本)賦值給子類型的原型。
function inheritPrototype(SubType,superType) { var prototype = object(superType.prototype); //創(chuàng)建對象 prototype.constructor = subType; //增強對象 subType.prototype = prototype; //指定對象 } function SuperType(name) { this.name = name; this.color = ["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); };
這個例子的高效率體現(xiàn)在它只調(diào)用了一次SuperType構(gòu)造函數(shù)。
JavaScript主要通過原型鏈實現(xiàn)繼承。原型鏈的構(gòu)建是通過將一個類型的實例賦值給另一個構(gòu)造函數(shù)的原型實現(xiàn)的。SubType.prototype=new SuperType();這樣,子類型就能夠訪問超類型的所有屬性和方法,這一點與基于類的繼承很相似。原型鏈的問題是對象實例共享所有繼承的屬性和方法,因此不適宜多帶帶使用。解決這個問題的技術是借用構(gòu)造函數(shù),即在子類型構(gòu)造函數(shù)的內(nèi)部調(diào)用超類型構(gòu)造函數(shù)SuperType.call(this,name); 這樣就可以做到每個實例都具有自己的屬性,同時還能保證只使用構(gòu)造函數(shù)模式來定義類型。使用最多的繼承模式是組合繼承,這種模式使用原型鏈繼承共享的屬性和方法,而借用構(gòu)造函數(shù)繼承實例屬性。
此外,還存在下列可供選擇的繼承模式:
原型式繼承,可以在不必預先定義構(gòu)造函數(shù)的情況下實現(xiàn)繼承,其本質(zhì)是執(zhí)行給定對象的淺復制。而復制的副本還可以得到進一步的改造。
寄生式繼承,與原型式繼承非常相似,也是基于某個對象或某些信息創(chuàng)建一個對象,然后增強對象,最后返回對象。為了解決組合繼承模式由于多次調(diào)用超類型函數(shù)而導致的低效率問題,可以將這個模式與組合繼承一起使用。
寄生組合式繼承,集寄生式繼承與組合繼承的優(yōu)點于一身,是實現(xiàn)基于類型繼承的最有效的方式
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://m.hztianpu.com/yun/109052.html
摘要:使用最多的繼承模式是組合繼承,這種模式使用原型鏈繼承共享的屬性和方法,而借用構(gòu)造函數(shù)繼承實例屬性。原型式繼承,可以在不必預先定義構(gòu)造函數(shù)的情況下實現(xiàn)繼承,其本質(zhì)是執(zhí)行給定對象的淺復制。 1、原型鏈實現(xiàn)繼承 function SuperType() { this.property = true; } SuperType.prototype.getSuperValue = func...
摘要:此時的原型對象包括一個指向另一個原型的指針,相應的,另一個原型中的指向另一個構(gòu)造函數(shù)。這種關系層層遞進,就通過一個原型對象鏈接另一個構(gòu)造函數(shù)的原型對象的方式實現(xiàn)了繼承。 讀這篇之前,最好是已讀過我前面的關于對象的理解和封裝類的筆記。第6章我一共寫了3篇總結(jié),下面是相關鏈接:讀《javaScript高級程序設計-第6章》之理解對象讀《javaScript高級程序設計-第6章》之封裝類 一...
摘要:構(gòu)造函數(shù)本身也是函數(shù),只不過可以用來創(chuàng)建對象而已。在創(chuàng)建子類型的實例時,沒有辦法在不影響所有對象實例的情況下,不能向超類型的構(gòu)造函數(shù)中傳遞參數(shù)。借用構(gòu)造函數(shù)又叫偽造對象或經(jīng)典繼承。 本章內(nèi)容 理解對象屬性 理解并創(chuàng)建對象 理解繼承 ECMA-262 把對象定義為:無序?qū)傩缘募?,其屬性可以包含基本值、對象或者函?shù)。嚴格來講,這就相當于說對象是一組沒有特定順序的值。 每個對象都是基于...
摘要:其中,描述符對象的屬性必須是和。吧設置為,表示不能從對象中刪除屬性。這個方法接收兩個對象參數(shù)要添加和修改其屬性值的對象,第二個是與第一個對象中要添加和修改的屬性值一一對應。 理解對象 1、創(chuàng)建自定義對象的兩種方法: (1)創(chuàng)建一個Object實例,然后再為它添加屬性和方法。 var person = new Object(); person.name = Nicholas; ...
閱讀 2613·2021-10-12 10:12
閱讀 1784·2019-08-30 15:52
閱讀 2509·2019-08-30 13:04
閱讀 1789·2019-08-29 18:33
閱讀 1026·2019-08-29 16:28
閱讀 506·2019-08-29 12:33
閱讀 2102·2019-08-26 13:33
閱讀 2418·2019-08-26 11:36