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