一說起JavaScript就要談的幾個問題,原型就是其中的一個。說了句大話,史上最清晰。本來是想按照大綱式的行文寫一下,但寫到後邊感覺其實就一個概念,沒有什麼條理性,所以下面就簡單按照概念解釋的模式談下這個問題。
1.JavaScript的原型是什麼?
原型,首先他是個對象。和在以對象為核心的JavaScript這門語言中的其他普通對象來說一樣,只不過他的角色有點特殊。但首先要明白他就是一個對象,是一個無序的屬性和值的序列對。
2.誰會具有原型這個對象?
所有的對象(包括函數這個對象)在默認的情況下都有一個原型對象。這句話的理解就和普通對象中再嵌套擁有對象是一個意思。
因為原型本身也是對象,所以每個原型自身又有一個原型對象(屬性繼承就是這麼來的)。照這句話下去的話會成為一個無限繼承的模式,所以一定會有一個結束點。所以例外來了,當對象原型追溯到原型鏈的最頂端的時候,這個最頂端的位置也就是在這種層層向上的關系到了Object這個構造函數的prototype,Object函數的的prototype對象如下圖:
從圖中可以看到,Object的屬性裡已經沒有了__proto__。至於什麼是__proto__,下邊會講。
3.__proto__和prototype分別是指什麼?
每一個對象都有一個__proto__對象屬性,每一個函數(函數也是對象)都有一個 prototype對象屬性,所以總結來說就是每一個對象有一個__proto__,而每一個函數都有一 個prototype,但同時他又是一個對象,所以函數也會有__proto__。
4.__proto__和prototype的關系是怎麼樣的?
首先明白對象是怎麼來的?對象無非是構造函數構造出來的或者是字面直接量的形式。
構造出來的對象的__proto__指向構造該對象的構造函數的prototype;字面直接量的對象的__proto__指向該對象類型所對應的的構造函數的prototype。
總結來說就是對象的__proto__指向該對象所對應的構造函數的prototype。
5.不同函數的prototype分別是什麼?
其實這是兩個問題,第一先回答不同函數。
首先JavaScript中只要是函數,都可以用new關鍵字當作構造函數來使用。但是,有些函數是我們自己實現的,還有一些函數是JavaScript語言標准裡已經規定內置實現的。這些函數包括Array、Boolean、Date、Number、String、RegExp、Math。這裡會有一個問題就是Math他是一個對象而不是函數,你可以去typeof檢測一下。除去Math之後剩余的這些內置構造函數的prototype屬性也是語言內置實現的,比如Array裡我們常用的那些pop、push、shift這些方法,都統統包括在Array這個構造函數的prototype對象屬性裡。除了上面提到的內置構造函數外,其余的函數都是我們自己定義的函數,他們也會有prototype屬性,但是如果我們不顯示的去給prototype賦值的話,他就是一個空對象。
接下來回答第二個問題,prototype是什麼。
其實上面已經說的很清楚了,prototype是函數的一個固有屬性,即只要是個函數都會有的屬性,內置的構造函數已經有實現好的prototype,它裡邊包括各種方法或者屬性。而我們自己實現的函數也必定會有一個prototype屬性,但是如果我們不顯示賦值的話,他就是一個空對象。
6.那我們所說的原型和原型繼承到底是什麼意思?
如果你一字不落的看完上邊的所有概念而且能理解到80%以上,就應該很自然的得出這個問題的答案了。
我們一般所說的原型就是對象的原型,對象的原型是指_proto_這個對象。而所有的對象的繼承也是通過他來實現的。第4個問答中已經很明確了,__proto__指向該對象的構造函數的prototype,而該對象的構造函數的prototype作為對象會擁有一個__proto__,而這個__proto__又指向他的構造函數的prototype,層層向上,直到__proto__指向Object這個最頂端的函數對象的prototype,也就是第二條的圖所示的便結束。
如上便是JavaScript通過原型繼承的一個解釋。
總結:我們該怎麼掌握原型這個概念。
掌握一個東西,無非也就是 是什麼,怎麼樣,怎麼用這三點。
是什麼。
首先明白__proto__和prototype,對象都會有__proto__,包括函數對象,函數對象除了__proto__,還擁有prototype。
原型是一個對象,指__proto__,每個對象都會擁有這個屬性。__proto__指向該對象構造函數的prototype。__proto__的層層向上追溯的過程就是繼承的過程,直到追溯到最頂端即Object這個構造函數的prototype。
怎麼樣。
怎麼樣也就是為什麼的意思,原型之所以存在,是基於JavaScript這門語言的面向對象思想來說的。像其他語言都會有OO和繼承等這些特性,JavaScript雖為腳本語言,靈活卻不失功用,這個是他實現面向對象和繼承的一個思想。可以簡單的理解為此。
怎麼用。
這個是個很大的話題,可以另開一篇文章來闡述。這裡只是簡單的舉幾個例子。
如果僅僅只是因為為了給一個實例添加屬性而使用原型是沒有多大意義的,這和直接添加屬性到這個實例是一樣的,假如我們已經創建了一個實例對象 ,我們想要繼承一個已經存在的對象的功能比如說Array,我們可以像下面這樣做:
var a = [1, 2]; a.__proto__ = Array.prototype; console.log(a.length); //2
又比如我們自己實現了一個構造函數,上面已經說到,自己實現的函數的默認prototype是一個空對象,所以由這個構造函數創建生成的對象的__proto__指向的是一個空對象。我們這時候就可以用prototype來繼承或者實現一些事情。
var MyFun = function (x, y) { this.x = x; this.y = y; }; MyFun.prototype.getArea = function () { return this.x * this. } var obj = new MyFun(2, 3); var area = obj.getArea(); console.log(area); //6
寫完才發現,可能這是史上最不清晰的闡述。但如果你對原型已經有了一知半解卻還是有點模糊的話,好好的從頭讀到結尾,你會發現,原來掌握這個概念也是這麼簡單。
完!祝好運。