在瀏覽本文之前首先明白什么是對(duì)象?,在JavaScript中我們會(huì)用typeof( )這個(gè)函數(shù),那么使用typeof( )輸出的值一般會(huì)有number、boolean、string、undefined、function、object等這些類型,那我們本文要研究的就是object和function這兩種類型,返回值是這兩種類型的就是我們本文所要研究的對(duì)象。
什么是對(duì)象?
那么對(duì)象該如何去定義呢?我個(gè)人認(rèn)為對(duì)象就是一些屬性的集合。舉個(gè)栗子??
var Laowang={
'name':'老王',
'feature':'熱心腸',
'skill':function( ){
alert('特長是修水管');
}
}
那么在上面的例子中'Laowang'就是一個(gè)對(duì)象,你肯定會(huì)有疑問關(guān)于我對(duì)對(duì)象的定義,在'Laowang'這個(gè)對(duì)象中出現(xiàn)了function方法,但是這個(gè)方法在'Laowang'這個(gè)對(duì)象中是以鍵值對(duì)的形式出現(xiàn)的,所以這個(gè)function就是'skill'這個(gè)屬性的屬性值。所以驗(yàn)證了對(duì)象就是一些屬性的集合這句話。
上面的例子很好理解,但是數(shù)組和函數(shù)好像不能這樣去定義屬性,但他們也是對(duì)象啊,不要迷惑,他們有自己定義屬性的方法。以函數(shù)為例:
var laowang=function( ){
alert('修水管');
}
laowang.skill='熱心腸';
laowang.skill2='愛串門';
laowang.skill3={
'a':'親切問候鄰居家孩子'
}
這不,function就被賦予了skill、skill2、skill3這三個(gè)屬性。
上面說到function和objec這兩個(gè)返回值是對(duì)象,既然都是對(duì)象,為什么返回的不是一個(gè)值。由于function和object的關(guān)系比較特殊所以返回的值不同,我在下文會(huì)詳細(xì)講到function和object的'特殊關(guān)系'。
function和object的關(guān)系
上文說道function和object都是對(duì)象,但是function的返回值是function而不是object,那么他倆之間肯定有某種'神秘的關(guān)系'。
function和object的關(guān)系其實(shí)就像'先有蛋還是先有雞'這種讓你抓狂的問題。function是object的一種,但是object又是由function創(chuàng)建的,什么,你要打我臉?
var arr=['a','b','c'];
var obj={
'name':'老王',
'age':'99'
}
以上兩個(gè)都是對(duì)象,但是都不是由function創(chuàng)建的,不要忘記了這種寫法只是用字面量的方式來創(chuàng)建對(duì)象的。這種寫法只是為了讓代碼更簡單明了更容易理解。歸根到底以上兩種對(duì)象是由function創(chuàng)建的,請(qǐ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出很多實(shí)例對(duì)象,構(gòu)造函數(shù)和我們平常自定義的函數(shù)沒有語法上的區(qū)別,區(qū)分就是構(gòu)造函數(shù)一般首字母是大寫的。
是不是感覺很亂?為什么function和object的關(guān)系是這樣的,不要慌張,耐心看完本文你就會(huì)豁然開朗。
原型(prototype)
上面扯了半天對(duì)象,到這里終于講到本文的主要內(nèi)容了--原型(prototype)。
那么prototype到底是什么呢?不要著急,讓我們一步步來。
上面我們說到function也是一種對(duì)象,現(xiàn)在對(duì)這個(gè)應(yīng)該沒有任何疑問了,如果有疑問請(qǐng)滑動(dòng)你的鼠標(biāo)從頭開始看!!
function作為對(duì)象,那么他肯定是若干屬性的集合,在JavaScript中,function默認(rèn)有一個(gè)屬性,這個(gè)屬性就是prototype,既然是屬性那么肯定有相對(duì)應(yīng)的屬性值,prototype的屬性值是一個(gè)對(duì)象,既然是對(duì)象,那么肯定是若干屬性的集合,這個(gè)對(duì)象里有一個(gè)默認(rèn)的屬性:constructor,這個(gè)屬性相當(dāng)于一個(gè)'指針',指向這個(gè)函數(shù)本身。
以下圖為例:

prototype既然作為對(duì)象,屬性的集合,不可能就只有constructor這一個(gè)屬性,肯定可以自定義的增加許多屬性,如上圖所示。
上圖還出現(xiàn)了person1這個(gè)實(shí)例函數(shù),他是由構(gòu)造函數(shù)Person實(shí)例化出來的,上文說到每個(gè)function都有prototype這個(gè)屬性,person1也不例外,他的prototype大家會(huì)發(fā)現(xiàn)和Person這個(gè)構(gòu)造函數(shù)的是一樣的,實(shí)例對(duì)象的原型指向的是其構(gòu)造函數(shù)的原型對(duì)象。我們?cè)倏匆欢未a:
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實(shí)例化出來的,而且我們也沒有給person1定義任何屬性,但是person1.name=='Nicholas';這是為什么?那我們就不得不說起-proto-這個(gè)屬性了,每個(gè)對(duì)象都有這個(gè)屬性,這個(gè)屬性一般是隱藏的我們看不到,但是并不妨礙我們?nèi)チ私馑?/p>
這個(gè)屬性指向了創(chuàng)建這個(gè)對(duì)象的構(gòu)造函數(shù)的prototype。即:person1._ proto_ ===Person.prototype,下面我們來看看這個(gè)'_ proto_'是什么鬼。
_ proto_,隱式原型
上文我們提到_ proto_,那到底這個(gè)_ 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
通過看上面的代碼會(huì)發(fā)現(xiàn)結(jié)果為true,你沒有看錯(cuò),這也不是巧合,這是必然的結(jié)果。
實(shí)質(zhì)上person1是被Person實(shí)例化出來的,那么person1._ proto_===Person.prototype,下面用圖給你展示一下:
上圖的o1和o2是由Object實(shí)例化出來的,他們的_ proto_指向的是Object.prototype,這就說明:每個(gè)對(duì)象都有一個(gè)_ proto_屬性,指向創(chuàng)建該對(duì)象的構(gòu)造函數(shù)的prototype。
那么你肯定會(huì)問'每個(gè)對(duì)象都有一個(gè)_ proto_屬性,指向創(chuàng)建該對(duì)象的構(gòu)造函數(shù)的prototype',那Object也是一個(gè)對(duì)象,肯定也有_ proto_屬性,那他指向誰?
關(guān)于Object._ proto_的指向問題很特殊,在這個(gè)Object._ proto_是個(gè)特例,它指向null,這個(gè)地方大家一定要牢記。
也許你還會(huì)有另一個(gè)疑問,函數(shù)也是對(duì)象,實(shí)例化出來的函數(shù)的_ proto_屬性指向其構(gòu)造函數(shù),那么其構(gòu)造函數(shù)的_ proto_指向誰?
Function這個(gè)前面沒有提到,現(xiàn)在拿出來曬曬,構(gòu)造函數(shù)是由誰創(chuàng)建的,就是由Function這個(gè)函數(shù)創(chuàng)建的,所以你上面的疑問就很好解答了。再用一張圖讓你更清晰的看清他們的關(guān)系:
這張圖清晰的表明了自定義構(gòu)造函數(shù)、Object、Function之間的關(guān)系!
眼神好的人會(huì)在上圖發(fā)現(xiàn)一個(gè)問題:自定義函數(shù)Foo._ proto_指向Function.prototype,Object._ proto_指向Function.prototype,怎么Function._ proto_也指向Function.prototype,這不就是形成了一個(gè)'死循環(huán)'么,來,讓我們仔細(xì)捋一捋,F(xiàn)unction也是一個(gè)函數(shù),既然是函數(shù)那么他肯定是由Function創(chuàng)建的,那么上面的'死循環(huán)'就解釋通了。
在這里我還要解釋一個(gè)地方,F(xiàn)unction.prototype也是一個(gè)對(duì)象,那其肯定有_ proto_屬性,那么指向誰呢?其指向Object.prototype,為什么呢?Function.prototype是一個(gè)普通的對(duì)象,就可以看成這個(gè)對(duì)象是由Object實(shí)例化出來的,那么Function.prototype._ proto_指向就是Object.prototype了。
下面上一張完整的圖片,大家可以按照下面這種圖片捋一下自己的思路,因?yàn)樯厦嬷v了那么多肯定會(huì)有些亂。
這張圖完整的呈現(xiàn)出了實(shí)例對(duì)象、自定義函數(shù)、Object、Function之間種種錯(cuò)綜復(fù)雜的關(guān)系,不要怕麻煩,一條一條的去找對(duì)應(yīng)的關(guān)系。
繼承
為什么會(huì)說到繼承呢,因?yàn)?strong>繼承是通過原型鏈來體現(xiàn)的,所以一并放在這里講了。我們先看一段代碼:
function Person(){ }
var p1=new Person();
Person.prototype.name='老王';
Person.prototype.age='99';
console.log(p1.name);//'老王'
以上代碼中,p1是Person實(shí)例化出來的函數(shù),我并沒有給p1定義name這個(gè)屬性,那p1.name是怎么來的--是從Person.prototype來的,因?yàn)閜1._ proto_指向Person.prototype,當(dāng)訪問對(duì)象的某個(gè)屬性時(shí),現(xiàn)在這個(gè)對(duì)象本身去找,如果找不到那就順著_ proto_往上找,直到找到或者Object.prototype為止。
由于所有的對(duì)象的原型鏈都會(huì)找到Object.prototype,因此所有的對(duì)象都會(huì)有Object.prototype的方法。這就是所謂的“繼承”。
講到這里,關(guān)于原型和原型鏈就結(jié)束了,希望各位能深刻的理解。