在瀏覽本文之前首先明白什么是對象?妖胀,在JavaScript中我們會用typeof( )這個函數(shù)芥颈,那么使用typeof( )輸出的值一般會有number、boolean赚抡、string爬坑、undefined、function涂臣、object等這些類型盾计,那我們本文要研究的就是object和function這兩種類型,返回值是這兩種類型的就是我們本文所要研究的對象赁遗。
什么是對象署辉?
那么對象該如何去定義呢?我個人認(rèn)為對象就是一些屬性的集合岩四。舉個栗子??
var Laowang={
'name':'老王',
'feature':'熱心腸',
'skill':function( ){
alert('特長是修水管');
}
}
那么在上面的例子中'Laowang'就是一個對象哭尝,你肯定會有疑問關(guān)于我對對象的定義,在'Laowang'這個對象中出現(xiàn)了function方法剖煌,但是這個方法在'Laowang'這個對象中是以鍵值對的形式出現(xiàn)的材鹦,所以這個function就是'skill'這個屬性的屬性值。所以驗證了對象就是一些屬性的集合這句話耕姊。
上面的例子很好理解桶唐,但是數(shù)組和函數(shù)好像不能這樣去定義屬性,但他們也是對象啊茉兰,不要迷惑莽红,他們有自己定義屬性的方法。以函數(shù)為例:
var laowang=function( ){
alert('修水管');
}
laowang.skill='熱心腸';
laowang.skill2='愛串門';
laowang.skill3={
'a':'親切問候鄰居家孩子'
}
這不邦邦,function就被賦予了skill安吁、skill2、skill3這三個屬性燃辖。
上面說到function和objec這兩個返回值是對象鬼店,既然都是對象,為什么返回的不是一個值黔龟。由于function和object的關(guān)系比較特殊所以返回的值不同妇智,我在下文會詳細(xì)講到function和object的'特殊關(guān)系'。
function和object的關(guān)系
上文說道function和object都是對象氏身,但是function的返回值是function而不是object巍棱,那么他倆之間肯定有某種'神秘的關(guān)系'。
function和object的關(guān)系其實就像'先有蛋還是先有雞'這種讓你抓狂的問題蛋欣。function是object的一種航徙,但是object又是由function創(chuàng)建的,什么陷虎,你要打我臉到踏?
var arr=['a','b','c'];
var obj={
'name':'老王',
'age':'99'
}
以上兩個都是對象杠袱,但是都不是由function創(chuàng)建的,不要忘記了這種寫法只是用字面量的方式來創(chuàng)建對象的窝稿。這種寫法只是為了讓代碼更簡單明了更容易理解楣富。歸根到底以上兩種對象是由function創(chuàng)建的,請看以下代碼:
var arr=new Arry('a','b','c');
var obj=new Object();
obj.name='老王';
obj.age='99';
在以上代碼中Arry( )和Object( )都是函數(shù)伴榔,通常我們把他們當(dāng)做構(gòu)造函數(shù)纹蝴,由構(gòu)造函數(shù)我們可以new出很多實例對象,構(gòu)造函數(shù)和我們平常自定義的函數(shù)沒有語法上的區(qū)別踪少,區(qū)分就是構(gòu)造函數(shù)一般首字母是大寫的骗灶。
是不是感覺很亂?為什么function和object的關(guān)系是這樣的秉馏,不要慌張耙旦,耐心看完本文你就會豁然開朗。
原型(prototype)
上面扯了半天對象萝究,到這里終于講到本文的主要內(nèi)容了--原型(prototype)免都。
那么prototype到底是什么呢?不要著急帆竹,讓我們一步步來绕娘。
上面我們說到function也是一種對象,現(xiàn)在對這個應(yīng)該沒有任何疑問了栽连,如果有疑問請滑動你的鼠標(biāo)從頭開始看O樟臁!
function作為對象秒紧,那么他肯定是若干屬性的集合绢陌,在JavaScript中,function默認(rèn)有一個屬性熔恢,這個屬性就是prototype脐湾,既然是屬性那么肯定有相對應(yīng)的屬性值,prototype的屬性值是一個對象叙淌,既然是對象秤掌,那么肯定是若干屬性的集合,這個對象里有一個默認(rèn)的屬性:constructor鹰霍,這個屬性相當(dāng)于一個'指針'闻鉴,指向這個函數(shù)本身。
以下圖為例:
![](https://ss0.bdstatic.com/94oJfD_bAAcT8t7mm9GUKT-xh_/timg?image&quality=100&size=b4000_4000&sec=1482826224&di=7b1bdd252ee65f02790281f8dd2d4f8c&src=http://images.cnitblog.com/blog/431232/201304/29082848-e2172b2bf28749de96d5eeed1ac2cba7.png)
prototype既然作為對象茂洒,屬性的集合孟岛,不可能就只有constructor這一個屬性,肯定可以自定義的增加許多屬性,如上圖所示蚀苛。
上圖還出現(xiàn)了person1這個實例函數(shù)在验,他是由構(gòu)造函數(shù)Person實例化出來的玷氏,上文說到每個function都有prototype這個屬性堵未,person1也不例外,他的prototype大家會發(fā)現(xiàn)和Person這個構(gòu)造函數(shù)的是一樣的盏触,實例對象的原型指向的是其構(gòu)造函數(shù)的原型對象渗蟹。我們再看一段代碼:
var Person=function(){};
Person.prototype.name='Nicholas';
Person.prototype.age='29';
Person.prototype.job='Software Engineer';
var person1=new Person();
console.log(person1.name); // 'Nicholas'
console.log(person1.age); // '29'
在上面代碼中person1是由構(gòu)造函數(shù)Person實例化出來的,而且我們也沒有給person1定義任何屬性赞辩,但是person1.name=='Nicholas';這是為什么雌芽?那我們就不得不說起-proto-這個屬性了,每個對象都有這個屬性辨嗽,這個屬性一般是隱藏的我們看不到世落,但是并不妨礙我們?nèi)チ私馑?/p>
這個屬性指向了創(chuàng)建這個對象的構(gòu)造函數(shù)的prototype。即:person1._ proto_ ===Person.prototype糟需,下面我們來看看這個'_ proto_'是什么鬼屉佳。
_ proto_,隱式原型
上文我們提到_ proto_,那到底這個_ proto_是什么呢洲押?我看下面的代碼:
var Person=function(){};
Person.prototype.name='Nicholas';
Person.prototype.age='29';
Person.prototype.job='Software Engineer';
var person1=new Person();
console.log(person1._proto_===Person.prototype);//true
通過看上面的代碼會發(fā)現(xiàn)結(jié)果為true武花,你沒有看錯,這也不是巧合杈帐,這是必然的結(jié)果体箕。
實質(zhì)上person1是被Person實例化出來的,那么person1._ proto_===Person.prototype挑童,下面用圖給你展示一下:
上圖的o1和o2是由Object實例化出來的累铅,他們的_ proto_指向的是Object.prototype,這就說明:每個對象都有一個_ proto_屬性站叼,指向創(chuàng)建該對象的構(gòu)造函數(shù)的prototype争群。
那么你肯定會問'每個對象都有一個_ proto_屬性,指向創(chuàng)建該對象的構(gòu)造函數(shù)的prototype'大年,那Object也是一個對象换薄,肯定也有_ proto_屬性,那他指向誰翔试?
關(guān)于Object._ proto_的指向問題很特殊轻要,在這個Object._ proto_是個特例,它指向null垦缅,這個地方大家一定要牢記冲泥。
也許你還會有另一個疑問,函數(shù)也是對象,實例化出來的函數(shù)的_ proto_屬性指向其構(gòu)造函數(shù)凡恍,那么其構(gòu)造函數(shù)的_ proto_指向誰志秃?
Function這個前面沒有提到,現(xiàn)在拿出來曬曬嚼酝,構(gòu)造函數(shù)是由誰創(chuàng)建的浮还,就是由Function這個函數(shù)創(chuàng)建的,所以你上面的疑問就很好解答了闽巩。再用一張圖讓你更清晰的看清他們的關(guān)系:
這張圖清晰的表明了自定義構(gòu)造函數(shù)钧舌、Object、Function之間的關(guān)系涎跨!
眼神好的人會在上圖發(fā)現(xiàn)一個問題:自定義函數(shù)Foo._ proto_指向Function.prototype洼冻,Object._ proto_指向Function.prototype,怎么Function._ proto_也指向Function.prototype隅很,這不就是形成了一個'死循環(huán)'么撞牢,來,讓我們仔細(xì)捋一捋叔营,F(xiàn)unction也是一個函數(shù)屋彪,既然是函數(shù)那么他肯定是由Function創(chuàng)建的,那么上面的'死循環(huán)'就解釋通了审编。
在這里我還要解釋一個地方撼班,F(xiàn)unction.prototype也是一個對象,那其肯定有_ proto_屬性垒酬,那么指向誰呢砰嘁?其指向Object.prototype,為什么呢?Function.prototype是一個普通的對象勘究,就可以看成這個對象是由Object實例化出來的矮湘,那么Function.prototype._ proto_指向就是Object.prototype了。
下面上一張完整的圖片口糕,大家可以按照下面這種圖片捋一下自己的思路缅阳,因為上面講了那么多肯定會有些亂。
這張圖完整的呈現(xiàn)出了實例對象景描、自定義函數(shù)十办、Object、Function之間種種錯綜復(fù)雜的關(guān)系超棺,不要怕麻煩向族,一條一條的去找對應(yīng)的關(guān)系。
繼承
為什么會說到繼承呢棠绘,因為繼承是通過原型鏈來體現(xiàn)的件相,所以一并放在這里講了再扭。我們先看一段代碼:
function Person(){ }
var p1=new Person();
Person.prototype.name='老王';
Person.prototype.age='99';
console.log(p1.name);//'老王'
以上代碼中,p1是Person實例化出來的函數(shù)夜矗,我并沒有給p1定義name這個屬性泛范,那p1.name是怎么來的--是從Person.prototype來的,因為p1._ proto_指向Person.prototype紊撕,當(dāng)訪問對象的某個屬性時罢荡,現(xiàn)在這個對象本身去找,如果找不到那就順著_ proto_往上找逛揩,直到找到或者Object.prototype為止柠傍。
由于所有的對象的原型鏈都會找到Object.prototype麸俘,因此所有的對象都會有Object.prototype的方法辩稽。這就是所謂的“繼承”。
講到這里从媚,關(guān)于原型和原型鏈就結(jié)束了逞泄,希望各位能深刻的理解。