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

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