javascript
JS面向对象,创建,继承
????
? ? ? 很開(kāi)心,最近收獲了很多知識(shí),而且發(fā)現(xiàn)很多東西,以前理解的都是錯(cuò)的,或者是膚淺的,還以為自己真的就get到了精髓,也很抱歉會(huì)影響一些人往錯(cuò)誤的道路上走,不過(guò)這也告訴了我們,看任何一篇文章都不能盲目的去相信,要實(shí)踐驗(yàn)證再驗(yàn)證。今天就重新整理一下,我對(duì)面向?qū)ο蟮睦斫?#xff0c;當(dāng)然也不保證完全正確的,但絕對(duì)是在進(jìn)步的,拋磚引玉,希望能帶來(lái)一些新的感悟。
? ? ?對(duì)象,通俗的來(lái)說(shuō),就是屬性和方法。定義就不再多說(shuō),下面說(shuō)對(duì)象的創(chuàng)建:
1 創(chuàng)建一個(gè)面向?qū)ο?/strong>
var?obj?=?new?Object();?//創(chuàng)建一個(gè)空對(duì)象obj.name?=?'haha'; obj.showName?=?function(){?alert(obj.name); } obj.showName();缺點(diǎn):當(dāng)我們想創(chuàng)建多個(gè)面向?qū)ο蟮臅r(shí)候,重復(fù)代碼過(guò)多,需要封裝,所以有了下面的方法
2 ?工廠方式
function?CreatePerson(name){?//原料var?obj?=?new?Object();????//加工obj.name?=?name;obj.showName?=?function(){?alert(this.name);}???//出廠return?obj; }var?p1?=?CreatePerson('haha'); p1.showName();var?p2?=?CreatePerson('hehe'); p2.showName();這其實(shí)就是簡(jiǎn)單的封裝函數(shù),整個(gè)過(guò)程像工廠的流水線(xiàn),所以叫工廠方式
缺點(diǎn):無(wú)法識(shí)別創(chuàng)建的對(duì)象的類(lèi)型。因?yàn)槿慷际荗bject,沒(méi)有區(qū)分度,不像Date、Array等,因此出現(xiàn)了構(gòu)造函數(shù)模式。
3 構(gòu)造函數(shù)模式
我們要通過(guò)這二個(gè)方面來(lái)改變:1 函數(shù)名首字母大寫(xiě) ?2 New 關(guān)鍵字調(diào)用
function?CreatePerson(name){?this.name?=?name;?this.showName?=?function(){?alert(this.name);?}? }? var?p1?=new?CreatePerson('haha');?p1.showName();var?p2?=?new?CreatePerson('hehe');p2.showName();1首字母大寫(xiě),是為了區(qū)別于普通的函數(shù),構(gòu)造函數(shù)本身就是普通的函數(shù),只是我們專(zhuān)門(mén)用它來(lái)實(shí)現(xiàn)了構(gòu)造的功能,所以專(zhuān)門(mén)起了一個(gè)名字叫構(gòu)造函數(shù),任何函數(shù)都可以成為構(gòu)造函數(shù),這取決于你調(diào)用函數(shù)的方式。是否用了New。
2 調(diào)用函數(shù)的時(shí)候用了 New關(guān)鍵字,那么New到底做了什么?用不用New有什么區(qū)別?再來(lái)看下面的例子
function?CreatePerson(name){???this.name?=?name;?this.showName?=?function(){?alert(this.name);?};console.log(this); }?new?CreatePerson('haha');?//CreatePerson{}CreatePerson('haha');??//window我們會(huì)發(fā)現(xiàn)當(dāng)用New去調(diào)用一個(gè)函數(shù)的時(shí)候,this的指向會(huì)不一樣。其實(shí)New主要做了下面這些事,不過(guò)下面寫(xiě)的只是大概的行為,并不是內(nèi)部源碼。
function?CreatePerson(name){???var?res?=?{};??//聲明一個(gè)空對(duì)象resres._proto_=?CreatePerson.prototype;//這個(gè)對(duì)象的_proto_屬性指向構(gòu)造函數(shù)的原型對(duì)象,這樣res就可以調(diào)用CreatePerson原型對(duì)象下的所有方法CreatePerson.apply(res);//把this指向改為res對(duì)象this.name?=?name;??//res對(duì)象添加屬性,方法this.showName?=?function(){?alert(this.name);?};??return?res;//返回這個(gè)對(duì)象}關(guān)于New做時(shí)候都是內(nèi)部的行為,看不到但確實(shí)存在,關(guān)于上面原型可以先大概知道結(jié)論,下面會(huì)說(shuō)原型,接著看就懂了。
函數(shù)構(gòu)造模式存在的問(wèn)題:
alert(p1.showName==p2.showName);//false測(cè)試這個(gè)代碼,兩個(gè)方法是不相同的,也就是說(shuō)這兩個(gè)對(duì)象并不是共用一個(gè)方法,每new一次,系統(tǒng)都會(huì)新創(chuàng)建一個(gè)內(nèi)存,這兩個(gè)對(duì)象各自有各自的地盤(pán),但他們具有相同的功能,還不共用,肯定不是我們所希望的。所以就有了下一種方法,原型+構(gòu)造模式
4 原型+構(gòu)造模式
每個(gè)函數(shù)都有一個(gè)prototype屬性,它是一個(gè)對(duì)象,也稱(chēng)作原型對(duì)象,這個(gè)原型對(duì)象,我們可以把方法和屬性寫(xiě)在它上面(不過(guò)原型對(duì)象不僅僅有我們寫(xiě)的屬性和方法,還有別的,下面會(huì)介紹),而通過(guò)這個(gè)函數(shù)創(chuàng)建出來(lái)的實(shí)例對(duì)象,都能共享這個(gè)原型對(duì)象下的方法和屬性。所以我們只需要把想要共享的東西放在函數(shù)的prototype下,不想共享的東西通過(guò)構(gòu)造函數(shù)來(lái)創(chuàng)建就可以了。
看個(gè)栗子(原型+構(gòu)造)
function?CreatePerson(name){?this.name?=?name; }CreatePerson.prototype.showName?=?function(){?alert(this.name); } var?p1?=new?CreatePerson('haha'); p1.showName(); var?p2?=?new?CreatePerson('hehe'); p2.showName();alert(p1.showName==p2.showName);//true? ? 通過(guò)最后一句的測(cè)試為true,可以看到在構(gòu)造函數(shù)的原型下面加的方法showName()方法是所有通過(guò)這個(gè)構(gòu)造函數(shù)創(chuàng)建出來(lái)的對(duì)象所共享的,也就是說(shuō)他們共用一個(gè)內(nèi)存,更進(jìn)一步的說(shuō)它們存在引用關(guān)系,也就是說(shuō)你更改了p1的showName也會(huì)影響p2的showName。
? ? 所以我們?cè)跇?gòu)造對(duì)象的時(shí)候,一般是原型模式和構(gòu)造模式組合使用,變化的用構(gòu)造模式 不變的公用的用原型模式,就像上面的這個(gè)栗子,屬性用的構(gòu)造函數(shù),因?yàn)橐话悴煌瑢?duì)象屬性都不同,方法用原型模式。
?_proto_屬性:同一個(gè)函數(shù)造出來(lái)的實(shí)例對(duì)象能共享這個(gè)函數(shù)的prototype下的方法和屬性,但是它是如何做到的呢?這里要出場(chǎng)的就是_proto_屬性,每個(gè)實(shí)例化對(duì)象都有一個(gè)_proto_屬性,它是一個(gè)指針,指向函數(shù)的prototype,也就是保存了它的地址。(JS中任何對(duì)象的值都是保存在堆內(nèi)存中,我們聲明的變量只是一個(gè)指針,保存了這個(gè)對(duì)象的實(shí)際地址,所以有了地址就能找到對(duì)象),所以總得來(lái)說(shuō),每個(gè)實(shí)例化對(duì)象都有_proto_屬性,保存了構(gòu)造函數(shù)的原型對(duì)象的地址,通過(guò)這個(gè)屬性就可以擁有原型對(duì)象下的所有屬性和方法,_proto_屬性實(shí)際就是實(shí)例化對(duì)象和原型對(duì)象之間的連接。
原型鏈: 每個(gè)函數(shù)都可以成為構(gòu)造函數(shù),每個(gè)函數(shù)都有原型對(duì)象,每個(gè)原型對(duì)象也可以是一個(gè)實(shí)例化對(duì)象,比如,你創(chuàng)建了一個(gè)函數(shù)fun,它是構(gòu)造函數(shù)function的實(shí)例化對(duì)象,而function的原型對(duì)象,又是Object的實(shí)例對(duì)象。所以fun有個(gè)_proto_屬性可以訪(fǎng)問(wèn)到function的原型對(duì)象,function原型對(duì)象也是個(gè)實(shí)例對(duì)象,也有個(gè)_proto_屬性,可以訪(fǎng)問(wèn)到Object的原型對(duì)象,所以通過(guò)_proto_屬性,就形成了一條原型鏈。每個(gè)實(shí)例化對(duì)象都可以訪(fǎng)問(wèn)到鏈子上方的方法和屬性,所以fun是可以訪(fǎng)問(wèn)Object原型對(duì)象下的方法和屬性的。實(shí)際上所有對(duì)象都可以訪(fǎng)問(wèn)到Object的原型對(duì)象。
原型鏈的訪(fǎng)問(wèn)規(guī)則:先在自身的下面尋找,再去一級(jí)一級(jí)的往原型鏈上找。如下:
function?Aaa(){} Aaa.prototype.num?=?3;var?a1?=?new?Aaa(); a1.num?=10; alert(a1.num);?//10原型對(duì)象下的方法和屬性:原型對(duì)象下面可能有三大類(lèi):1 原型對(duì)象所帶方法和屬性 ? 2 constructor ? 3 _proto_
constructor:構(gòu)造函數(shù)屬性,每個(gè)函數(shù)的原型對(duì)象都有的默認(rèn)屬性,指向函數(shù)。每個(gè)實(shí)例化對(duì)象本身是沒(méi)有constructor屬性的,每個(gè)實(shí)例化對(duì)象下面都默認(rèn)只有一個(gè)_proto_,用來(lái)連接原型對(duì)象,而和構(gòu)造函數(shù)本身是沒(méi)有直接的聯(lián)系的。所以它的constructor是訪(fǎng)問(wèn)的原型對(duì)象上的。所以當(dāng)原型對(duì)象的constructor變化了,實(shí)例化對(duì)象的constructor也會(huì)改變。但是如果這個(gè)對(duì)象本身既是原型對(duì)象,又是實(shí)例化對(duì)象,那就擁有了constructor屬性,無(wú)需從原型對(duì)象繼承。
看下面的例子,來(lái)驗(yàn)證我們所說(shuō)的:
?CreatePerson(name){???.name?=?name; } CreatePerson.prototype.showName?=?(){console.log(.name); };?p1?=?CreatePerson('haha'); p1.showName(); console.log(p1.constructor);??console.log(CreatePerson.prototype);?console.log(CreatePerson.prototype.__proto__===Object.prototype);console.log(CreatePerson.prototype.__proto__===Object);console.log(CreatePerson.prototype.constructor);console.log(Object.prototype.__proto__);?console.log(CreatePerson.__proto__);???console.log(CreatePerson.constructor);?console.log(CreatePerson.prototype??CreatePerson?)字面量法定義原型:
為了創(chuàng)建對(duì)象的代碼更方便,你一定見(jiàn)過(guò)這樣的代碼,就是字面量法:
function?Aaa(){} Aaa.prototype?=?{showName:function(){alert(10);} };?var?a1?=?new?Aaa(); console.log(Aaa.prototype);//{showName:function(){},_proto_}???你會(huì)發(fā)現(xiàn)constructor不見(jiàn)了,因?yàn)檫@種方式相當(dāng)于重新賦值了Aaa.prototype?console.log(Aaa.prototype.constructor);//Object??因?yàn)樽陨頉](méi)有了constructor屬性,就去上級(jí)原型對(duì)象找,找到了Objectconsole.log(a1.constructor?);//Object?也變了,驗(yàn)證了它是訪(fǎng)問(wèn)的原型對(duì)象上的因此我們?cè)趯?xiě)的時(shí)候需要修正一下原型的指向:function?Aaa(){} Aaa.prototype?=?{constructor:Aaa,num1:function(){alert(10);} }? var?a1?=?new?Aaa();? a1.constructor?//?Aaa?理解了這些,繼承就很好理解了,下回繼續(xù)補(bǔ)充。有錯(cuò)誤,歡迎糾正。
?
?
轉(zhuǎn)載于:https://blog.51cto.com/12879490/1921937
與50位技術(shù)專(zhuān)家面對(duì)面20年技術(shù)見(jiàn)證,附贈(zèng)技術(shù)全景圖總結(jié)
以上是生活随笔為你收集整理的JS面向对象,创建,继承的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: CC2530之Flash笔记
- 下一篇: 不在JPA 的 persistence.