摘要:因?yàn)檫@造成了繼承鏈的紊亂,因?yàn)榈膶?shí)例是由構(gòu)造函數(shù)創(chuàng)建的,現(xiàn)在其屬性卻指向了為了避免這一現(xiàn)象,就必須在替換對(duì)象之后,為新的對(duì)象加上屬性,使其指向原來(lái)的構(gòu)造函數(shù)。這個(gè)函數(shù)接收兩個(gè)參數(shù)子類(lèi)型構(gòu)造函數(shù)和超類(lèi)型構(gòu)造函數(shù)。
最近一直在研究js面向?qū)ο?,原型鏈繼承是一個(gè)難點(diǎn),下面是我對(duì)繼承的理解
以下文章借鑒自CSDN季詩(shī)筱的博客
ES中描述了原型鏈的概念,并將原型鏈作為實(shí)現(xiàn)繼承的主要方法;
基本思想:利用一個(gè)引用類(lèi)型繼承另一個(gè)引用類(lèi)型的屬性和方法:
簡(jiǎn)單回顧下: 構(gòu)造函數(shù) -- 原型 -- 實(shí)例 三者之間的關(guān)系
構(gòu)造函數(shù):function Person(){}
每個(gè)構(gòu)造函數(shù)都有一個(gè)原型對(duì)象(Person.prototype),
原型對(duì)象都包含一個(gè)指向構(gòu)造函數(shù)的指針(constructor),
(其實(shí)原型對(duì)象也是一個(gè)對(duì)象,也有一個(gè) __proto__ 指針,指向他所繼承的對(duì)象)
而實(shí)例都包含著一個(gè)指向圓形對(duì)象的內(nèi)部指針([[prototye]] 又稱(chēng)__proto__);
每個(gè)實(shí)例也有一個(gè)constructor屬性默認(rèn)調(diào)用原型對(duì)象的constructor屬性(!?。。?/p>
讓原型對(duì)象等于另一個(gè)構(gòu)造函數(shù)的實(shí)例, 顯然,此時(shí)的原型對(duì)象將包含一個(gè)指向另一個(gè)原型對(duì)象的指針([[prototype]]),從而擁有該原型對(duì)象的屬性和方法
另一個(gè)原型對(duì)象中也包含著指向另一個(gè)構(gòu)造函數(shù)的指針。那么上述關(guān)系依然成立,如此層層遞進(jìn),就構(gòu)成了實(shí)例與原型的鏈條。這就是所謂原型鏈的基本概念!
如上如所示,這種關(guān)系直到 當(dāng)某個(gè)原型對(duì)象的 contructor屬性指向 Object 為止
1.原型鏈繼承基本模式:function SuperType(){ this.prototype = 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
以上代碼定義了兩個(gè)類(lèi)型:SuperType 和 SubType.
每個(gè)類(lèi)型分別有一個(gè)屬性和方法。
他們的主要區(qū)別是SubType繼承了SuperType,而繼承是通過(guò)創(chuàng)建SuperType的實(shí)例,并將該實(shí)例賦值給SubType.prototype實(shí)現(xiàn)的。
實(shí)現(xiàn)的本質(zhì)是重寫(xiě)原型對(duì)象,代之以一個(gè)新類(lèi)型的實(shí)例。
換句話(huà)說(shuō),原來(lái)存在于SuberType的實(shí)例中的所有屬性和方法,現(xiàn)在存在于SubType.prototype中了。
在確立了繼承關(guān)系之后,我們給SubType.prototype添加了一個(gè)方法,,這樣就在繼承了SuperType的屬性和方法的基礎(chǔ)上又添加了一個(gè)新方法。
這個(gè)例子的實(shí)例以及構(gòu)造函數(shù)和原型之間的關(guān)系如下圖所示
圖:
在上面的代碼中,我們沒(méi)有使用SubType默認(rèn)提供的原型,而是給他換了一個(gè)新原型;
這個(gè)原型就是SuperType的實(shí)例。
于是,新原型不僅有作為一個(gè)SuperType的實(shí)例所擁有的全部屬性和方法,而且內(nèi)部還有一個(gè)指針,指向了SuperType的原型。
最終結(jié)果是這樣的:instace指向SubType的原型,SubType的原型又指向SuperType的原型。
在通過(guò)原型鏈繼承的情況下,搜索過(guò)程就得以沿著原型鏈繼續(xù)向上。就拿上面的例子來(lái)說(shuō),調(diào)用instance.getSuperValue()會(huì)經(jīng)歷三個(gè)步驟:
1.搜索實(shí)例
2.搜索SubType.prototype
3.搜索SuperType.prototype
最后一步才會(huì)找到該方法,再找不到該屬性或方法的情況下,搜索過(guò)程總是要一環(huán)一環(huán)地前行到原型鏈末端才會(huì)停下來(lái).
別忘記默認(rèn)原型Object
事實(shí)上前面例子中展示的原型鏈還少一環(huán),我們知道,所有引用類(lèi)型都默認(rèn)繼承了Object,而這個(gè)繼承也是通過(guò)原型鏈實(shí)現(xiàn)的。
大家記住,所有函數(shù)的默認(rèn)原型都是Object的實(shí)例,因此默認(rèn)原型內(nèi)部都會(huì)包含一個(gè)指針,指向Object.prototype。這也正是所有自定義類(lèi)型都會(huì)繼承toString()等默認(rèn)方法的根本原因.
原型鏈雖然很強(qiáng)大,可以用它實(shí)現(xiàn)繼承,但也存在一些問(wèn)題。
其中,最主要的問(wèn)題來(lái)自包含引用類(lèi)型值的原型。
想必大家還記得,我們前面介紹過(guò)包含引用類(lèi)型值的屬性會(huì)被所有實(shí)例共享;而這也正是為什么要在構(gòu)造函數(shù)中,而不是在原型對(duì)象中定義屬性的原因。
在通過(guò)原型來(lái)實(shí)現(xiàn)繼承時(shí),原型實(shí)際上會(huì)變成另一個(gè)類(lèi)型的實(shí)例。于是原先的實(shí)例屬性也就順理成章地變成了現(xiàn)在的原型屬性了.
例子說(shuō)明問(wèn)題:
function SuperType(){ this.colors = ["red","blue","green"]; } function SubType(){ } SubType.prototype = new SuperType(); // 繼承了SuperType 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,black
這個(gè)例子中的SuperType構(gòu)造函數(shù)定義了一個(gè)colors屬性,該屬性包括一個(gè)數(shù)組(引用類(lèi)型值)。SuperType的每個(gè)實(shí)例都會(huì)有各自包含自己數(shù)組的colors屬性。當(dāng)SubType通過(guò)原型鏈繼承了SuperType之后,SubType.prototype就變成了SuperType的一個(gè)實(shí)例,因此他也擁有了一個(gè)他自己的colors屬性-----就跟專(zhuān)門(mén)創(chuàng)建了一個(gè)SubType.prototype.colors屬性一樣。
但結(jié)果是什么呢? 所有實(shí)例都會(huì)共享這個(gè)colors屬性?。。?/p>
問(wèn)題1:結(jié)果是SubType的所有實(shí)例都會(huì)共享這一個(gè)colors屬性。而我們對(duì)instance1.colors的修改能夠通過(guò)instance2.colors反映出來(lái),就已經(jīng)充分證明這一點(diǎn)了
問(wèn)題2:在創(chuàng)建自定義類(lèi)型的時(shí)候,不能向超類(lèi)型的構(gòu)造函數(shù)中傳遞參數(shù)。
實(shí)際上,應(yīng)該說(shuō)是沒(méi)有辦法在不影響所有實(shí)例的情況下,給超類(lèi)型構(gòu)造函數(shù)傳遞參數(shù)。
有鑒于此,在加上前面剛剛討論的由于原型中所包含引用類(lèi)型值所帶來(lái)的問(wèn)題,實(shí)踐中中很少多帶帶使用原型鏈
1.借用構(gòu)造函數(shù)
2.組合式繼承
3.原型式繼承
4.寄生式繼承
5.寄生組合式繼承
組合式繼承:這里來(lái)談一下最常用的組合式繼承和寄生組合式繼承
組合繼承,有時(shí)候也叫做經(jīng)典繼承,指的是將原型鏈和借用構(gòu)造函數(shù)的技術(shù)組合到一起,從而發(fā)揮二者之長(zhǎng)的一種繼承模式。
其背后的思路是使用原型鏈實(shí)現(xiàn)對(duì)原型屬性和方法的繼承,而通過(guò)借用構(gòu)造函數(shù)來(lái)實(shí)現(xiàn)對(duì)實(shí)例的屬性的繼承
請(qǐng)看例子:
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); //繼承屬性, 第二次調(diào)用SuperType this.age = age; } //繼承方法 SubType.prototype = new SuperType(); //第一次調(diào)用SuperType SubType.prototype.constructor = SubType(); // 相當(dāng)重要,此處 SubType.prototype.sayAge = function(){ alert(this.age); } var instance1 = new SubType("leo",29); instance1.colors.push("black"); alert(instance1.colors); // r,b,g,b instance1.sayName(); // leo instance1.sayAge(); // 29 var instance2 = new SubType("lck",34); alert(instance2.colors); // r,b,g instance2.sayName(); // lck instance2.sayAge(); // 34
在例子中,SuperType構(gòu)造函數(shù)定義了兩個(gè)屬性:name 和 colors。SuperType的原型定義了一個(gè)方法sayName()。
SubType構(gòu)造函數(shù)在調(diào)用SuperType構(gòu)造函數(shù)傳入了name參數(shù),緊接著又定義了他自己的屬性age,然后,將SuperType的實(shí)例賦值給SubType的原型,然后又在該新原型上定義了方法sayAge()方法,
這樣一來(lái),就可以讓兩個(gè)不同的SubType實(shí)例既分別擁有自己屬性和公共的colors屬性,又可以使用相同的方法
組合繼承避免了原型鏈和借用構(gòu)造函數(shù)的缺陷,融合了他們的優(yōu)點(diǎn),成為js中最常用的繼承模式。
而且 instanceof和isPrototypeOf()也能夠用于識(shí)別基于組合繼承創(chuàng)建的對(duì)象
**另外說(shuō)一下:
1.任何一個(gè)Prototype對(duì)象都有一個(gè)constructor指針,指向它的構(gòu)造函數(shù);
2.每個(gè)實(shí)例中也會(huì)有一個(gè)constructor指針,這個(gè)指針默認(rèn)調(diào)用Prototype對(duì)象的constructor屬性。
結(jié)果:當(dāng)替換了子類(lèi)的原型之后,即 SubType.prototype = new SuperType()之后,
SubType.prototype.constructor 就指向了SuperType(),
SubType的實(shí)例的constructor也指向了SuperType(),這就出現(xiàn)問(wèn)題了。
因?yàn)檫@造成了繼承鏈的紊亂,因?yàn)镾ubType的實(shí)例是由SubType構(gòu)造函數(shù)創(chuàng)建的,現(xiàn)在其constructor屬性卻指向了SuperType,為了避免這一現(xiàn)象,就必須在替換prototype對(duì)象之后,為新的prototype對(duì)象加上constructor屬性,使其指向原來(lái)的構(gòu)造函數(shù)。
組合式繼承的缺點(diǎn)
組合繼承最大的問(wèn)題就是無(wú)論在什么情況下,都會(huì)兩次調(diào)用超類(lèi)型構(gòu)造函數(shù);
一次是在創(chuàng)建子類(lèi)型的原型的時(shí)候,
另一次是在子類(lèi)型構(gòu)造函數(shù)內(nèi)部。
沒(méi)錯(cuò)子類(lèi)型最終會(huì)包含超類(lèi)型對(duì)象的全部實(shí)例屬性,但我們不得不在調(diào)用子類(lèi)構(gòu)造函數(shù)時(shí)重寫(xiě)這些屬性
此種模式解決了組合式繼承的缺點(diǎn)
原理:通過(guò)借用構(gòu)造函數(shù)來(lái)繼承屬性,通過(guò)原型鏈的混成形式來(lái)繼承方法。
思路:不必為了指定子類(lèi)的原型而調(diào)用超類(lèi)型的構(gòu)造函數(shù),我們所需要的無(wú)非就是超類(lèi)型原型的一個(gè)副本而已。
寄生式組合繼承的基本模式如下所示:
function inheritPrototype(subType,superType){ var prototype = object(superType.prototype); // 創(chuàng)建對(duì)象 prototype.constructor = subType; // 增強(qiáng)對(duì)象 subType.prototype = prototype; // 指定對(duì)象 }
這個(gè)示例中的inheritPrototype()函數(shù)實(shí)現(xiàn)了寄生式組合繼承的最簡(jiǎn)單形式。
這個(gè)函數(shù)接收兩個(gè)參數(shù):子類(lèi)型構(gòu)造函數(shù)和超類(lèi)型構(gòu)造函數(shù)。
在函數(shù)內(nèi)部:
第一步:是創(chuàng)建超類(lèi)型原型的一個(gè)副本
第二步:為創(chuàng)建的副本添加constructor屬性,從而彌補(bǔ)因失去原型而失去的默認(rèn)的constructor屬性
第三步:將創(chuàng)建的對(duì)象(即副本)賦值給子類(lèi)型的原型。
這樣,我們就可以調(diào)用inherit-Protoype()函數(shù)的語(yǔ)句,去替換前面例中為了子類(lèi)型原型賦值的語(yǔ)句了
實(shí)例如下:
//借助原型可以基于已有對(duì)象創(chuàng)建新對(duì)象 function object(o){ var F = function(){}; // 創(chuàng)建一個(gè)空對(duì)象 F.prototype = o; return new F(); //返回出一個(gè)實(shí)例對(duì)象 } //寄生式組合繼承 function inheritPrototype(subType, superType){ var prototype = object(superType.prototype); //創(chuàng)建父構(gòu)造函數(shù)實(shí)例對(duì)象副本 prototype.constructor = subType; // 重寫(xiě)將子類(lèi)原型對(duì)象的constructor屬性 subType.prototype = prototype; // 父類(lèi)實(shí)例賦值給子類(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); this.age = age; } //調(diào)用此方法代替前面賦值語(yǔ)句,可解決兩次調(diào)用超類(lèi)型構(gòu)造函數(shù)的問(wèn)題 inheritPrototype(SubType, SuperType); SubType.prototype.sayAge = function(){ alert(this.age); }; var instance1 = new SubType("lck",29); console.log(instance1.name,instance1.age,instance1.colors); // lck,29,r,b,g instance1.sayName(); // lck instance1.sayAge() // 29
優(yōu)點(diǎn):
這個(gè)例子的高效率體現(xiàn)在他只調(diào)用了一次SuperType構(gòu)造函數(shù),
并且因此避免了在SubType.prototype上面創(chuàng)建不必要的,多余的屬性。
與此同時(shí),原型鏈還能保持不變;
因此,還能正常使用instanceof 和 isPrototypeOf()。
開(kāi)發(fā)人員普遍認(rèn)為寄生式組合式繼承是引用類(lèi)型最理想的繼承范式
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/81113.html
摘要:是完全的面向?qū)ο笳Z(yǔ)言,它們通過(guò)類(lèi)的形式組織函數(shù)和變量,使之不能脫離對(duì)象存在。而在基于原型的面向?qū)ο蠓绞街?,?duì)象則是依靠構(gòu)造器利用原型構(gòu)造出來(lái)的。 JavaScript 函數(shù)式腳本語(yǔ)言特性以及其看似隨意的編寫(xiě)風(fēng)格,導(dǎo)致長(zhǎng)期以來(lái)人們對(duì)這一門(mén)語(yǔ)言的誤解,即認(rèn)為 JavaScript 不是一門(mén)面向?qū)ο蟮恼Z(yǔ)言,或者只是部分具備一些面向?qū)ο蟮奶卣?。本文將回歸面向?qū)ο蟊疽?,從?duì)語(yǔ)言感悟的角度闡述為什...
摘要:對(duì)象重新認(rèn)識(shí)面向?qū)ο竺嫦驅(qū)ο髲脑O(shè)計(jì)模式上看,對(duì)象是計(jì)算機(jī)抽象現(xiàn)實(shí)世界的一種方式。除了字面式聲明方式之外,允許通過(guò)構(gòu)造器創(chuàng)建對(duì)象。每個(gè)構(gòu)造器實(shí)際上是一個(gè)函數(shù)對(duì)象該函數(shù)對(duì)象含有一個(gè)屬性用于實(shí)現(xiàn)基于原型的繼承和共享屬性。 title: JS對(duì)象(1)重新認(rèn)識(shí)面向?qū)ο? date: 2016-10-05 tags: JavaScript 0x00 面向?qū)ο?從設(shè)計(jì)模式上看,對(duì)象是...
摘要:很多情況下,通常一個(gè)人類(lèi),即創(chuàng)建了一個(gè)具體的對(duì)象。對(duì)象就是數(shù)據(jù),對(duì)象本身不包含方法。類(lèi)是相似對(duì)象的描述,稱(chēng)為類(lèi)的定義,是該類(lèi)對(duì)象的藍(lán)圖或原型。在中,對(duì)象通過(guò)對(duì)類(lèi)的實(shí)體化形成的對(duì)象。一類(lèi)的對(duì)象抽取出來(lái)。注意中,對(duì)象一定是通過(guò)類(lèi)的實(shí)例化來(lái)的。 showImg(https://segmentfault.com/img/bVTJ3H?w=900&h=385); 馬上就要到七夕了,離年底老媽老爸...
摘要:很多情況下,通常一個(gè)人類(lèi),即創(chuàng)建了一個(gè)具體的對(duì)象。對(duì)象就是數(shù)據(jù),對(duì)象本身不包含方法。類(lèi)是相似對(duì)象的描述,稱(chēng)為類(lèi)的定義,是該類(lèi)對(duì)象的藍(lán)圖或原型。在中,對(duì)象通過(guò)對(duì)類(lèi)的實(shí)體化形成的對(duì)象。一類(lèi)的對(duì)象抽取出來(lái)。注意中,對(duì)象一定是通過(guò)類(lèi)的實(shí)例化來(lái)的。 showImg(https://segmentfault.com/img/bVTJ3H?w=900&h=385); 馬上就要到七夕了,離年底老媽老爸...
摘要:很多情況下,通常一個(gè)人類(lèi),即創(chuàng)建了一個(gè)具體的對(duì)象。對(duì)象就是數(shù)據(jù),對(duì)象本身不包含方法。類(lèi)是相似對(duì)象的描述,稱(chēng)為類(lèi)的定義,是該類(lèi)對(duì)象的藍(lán)圖或原型。在中,對(duì)象通過(guò)對(duì)類(lèi)的實(shí)體化形成的對(duì)象。一類(lèi)的對(duì)象抽取出來(lái)。注意中,對(duì)象一定是通過(guò)類(lèi)的實(shí)例化來(lái)的。 showImg(https://segmentfault.com/img/bVTJ3H?w=900&h=385); 馬上就要到七夕了,離年底老媽老爸...
摘要:除了以上介紹的幾種對(duì)象創(chuàng)建方式,此外還有寄生構(gòu)造函數(shù)模式穩(wěn)妥構(gòu)造函數(shù)模式。 showImg(https://segmentfault.com/img/remote/1460000018196128); 面向?qū)ο?是以 對(duì)象 為中心的編程思想,它的思維方式是構(gòu)造。 面向?qū)ο?編程的三大特點(diǎn):封裝、繼承、多態(tài): 封裝:屬性方法的抽象 繼承:一個(gè)類(lèi)繼承(復(fù)制)另一個(gè)類(lèi)的屬性/方法 多態(tài):方...
閱讀 710·2021-11-11 16:55
閱讀 2243·2021-11-11 16:55
閱讀 2042·2021-11-11 16:55
閱讀 2412·2021-10-25 09:46
閱讀 1685·2021-09-22 15:20
閱讀 2438·2021-09-10 10:51
閱讀 1791·2021-08-25 09:38
閱讀 2695·2019-08-30 12:48