JavaScript原型概念說不好理解其實沒那麼難,說好理解其實也沒那麼簡單,關鍵是你有沒有找到一個合適自己的理解方法。
預備知識:
我想研究js原型的人肯定對js有一定瞭解,所以基礎的知識不再贅述。但是我還要強調一些變態的知識點:
l JavaScript沒有類的概念,是基於原型的物件導向。
l function這個關鍵字一定要理解正確!這不是傳統意義上的定義函數的關鍵字,而是類!一定要把function理解成類!所以,在function a(){}時,其實你是在建立一個functio的執行個體,a不是函數,而是對象!這正好符合一句真理:“一切皆對象”。
l new關鍵字也不是傳統的new。後邊加的是對象,而不是類。
l JavaScript中沒有方法,只有屬性。
l .表示訪問(擷取成員屬性),()表示執行。比如fun.x()可以理解為:fun這個對象訪問了x屬性,並執行這個屬性。
概念描述(部分內容來自於網路):
在說原型之前,還要先說點別的知識。上邊提到function實際上是在建立對象,這時候它充當了兩個角色:建立對象、建構函式。JavaScript就是這麼規定的,所有的對象在建立時都要有建構函式。還有需要注意的就是new關鍵字後邊接的全是對象,並不是類,new的功能和傳統的語言也不一樣,因為JavaScript是基於原型的,沒有類,所以new不可能完成傳統new的功能,那它做什麼了呢?我就簡單的說一句:new其實就實現了原型機制(具體怎麼做的網上資料有的是)。
究竟什麼是原型,下邊是來自網路的一段話:
“javascript原型是一個對象。 javascript中所有的建構函式都有一個屬性,叫prototype,這個屬性存放的就是原型對象;訪問這個屬性通過:函數名.prototype ,從而可以訪問到這個原型對象,也可以為這個原型賦值。在原型對象中有一個屬性叫constructor,這個constructor指向函數本身。我們可以訪問到原型中的這個屬性:函數名.prototype.constructor;我們可以為原型對象添加屬性並賦值:函數名.prototype.屬性名稱=值;為函數原型添加的屬性,都會成為建構函式的屬性,從而成為對象的屬性。既然對象具有了原型內的屬性,說明對象中的這些屬性是從原型中繼承來的。所以javascript是基於原型的繼承的。”
其實原型就是對象的prototype屬性指向的對象。對象通過prototype屬性繼承了“父類”的特性,甚至包括建構函式在內。證明繼承了建構函式的例子:
<script type="text/javascript">function fun1(){alert("我愛中國");}//fun1的建構函式fun2 = new fun1();//通過fun1建立fun2fun3 = new fun2();//我們並沒有定義fun2的建構函式,但是成功的建立了fun3,說明構造方法也會繼承fun3();//輸出“我愛中國”</script>
現在再回過頭來看new和function,我們可以通過function和new兩種方式建立對象,但兩者都要求必須有建構函式,只不過function同時承擔了建立對象和建構函式兩個職責,而new卻要找“父類”的建構函式(系統內部的對象比如Array也有建構函式,這樣我們才可以順利的new,只不過隱藏了)。此時,應該不難理解new實際上是把“父類”作為原型給“子類”的prototype屬性。而function是建立,以自己為原型給自己的prototype屬性。
這差不多就是JavaScript的原型繼承機制了!繼續深入,上文提到JavaScript沒有方法,只有屬性。實際上所有的對象都可以理解為一個屬性包,或者說散列數組(key,value),對象只有訪問成員的操作。通過“.”指定要訪問的屬性,通過“()”執行屬性(可以理解為方法),JavaScript並不管我們要執行的屬性是什麼,只負責通過key(屬性名稱)找到相應的value(屬性值),所以對象的屬性值可以是對象,也可以是數值,是對象的話我們可以用“()”執行,是數值型的話就直接“.”+屬性名稱訪問。
原型鏈:
我們可以自行更改prototype屬性,也就是自行設定對象的原型對象。同時,原型繼承是鏈式的。比如,b繼承了a,c又繼承了b,那麼c也繼承了a,這樣就構成了原型鏈。原型鏈的特性是:讀的時候從原型鏈上讀,寫的時候向自己裡面寫。大致意思就是:在剛剛abc的例子中,如果在c中訪問了一個屬性,但是c中沒有,會自動去b、a中尋找;但是如果向c中添加屬性,那麼就直接寫在c中。
範例程式碼:
<script type="text/javascript">function fun1(){}//建立fun1對象,原型為自身function fun2(){}//建立fun2對象,原型為自身fun1.prototype = new Object();//設對象fun1的原型為最底層的系統的Object對象fun2.prototype = new fun1();//設對象fun2的原型為fun1Object.prototype.a="a";//給對象Object的原型添加一個數值型a屬性fun = new fun2();//建立一個fun2的對象,繼承fun2的所有屬性alert(fun.a);//調用fun2繼承來的a屬性,實際上調用的Object原型中的a。列印出“a”fun1.prototype.a = "a1";//給fun1的原型(即Object)對象添加一個數值型a屬性alert(fun.a);//調用fun2繼承來的a屬性,實際上調用的fun1原型(即Object)中的a。列印出“a1”</script>
圖解: