原型
在使用JavaScript的面向?qū)ο缶幊讨校蛯ο笫莻€(gè)核心概念。在JavaScript中對象是作為現(xiàn)有示例對象(即原型)的副本而創(chuàng)建的,該名稱就來自于這一概念。此原型對象的任何屬性和方法都將顯示為從原型的構(gòu)造函數(shù)創(chuàng)建的對象的屬性和方法。可以說,這些對象從其原型繼承了屬性和方法。當(dāng)您創(chuàng)建如下所示的新Dog對象時(shí):
var buddy = new Dog('Buddy');
buddy所引用的對象將從它的原型繼承屬性和方法,盡管僅從這一行可能無法明確判斷原型來自哪里,對象buddy的原型來自構(gòu)造函數(shù)(在這里是函數(shù)Dog)的屬性。
在JavaScript中,每個(gè)函數(shù)都有名為"prototype"的屬性,用于引用原型對象。此原型對象又有名為"constructor"的屬性,它反過來引用函數(shù)本身。這是一種循環(huán)引用,圖3更好地說明了這種循環(huán)關(guān)系。
現(xiàn)在,通過"new"運(yùn)算符用函數(shù)(上面示例中為Dog)創(chuàng)建對象時(shí),所創(chuàng)建的對象將繼承Dog.prototype屬性。在圖3中,可以看到Dog.prototype對象有一個(gè)回指Dog函數(shù)的構(gòu)造函數(shù)屬性。這樣,每個(gè)Dog對象(從Dog.prototype繼承而來)都有一個(gè)回指Dog函數(shù)的構(gòu)造函數(shù)屬性。圖4中的代碼證實(shí)了這一點(diǎn)。圖5顯示了構(gòu)造函數(shù)、原型對象以及它們創(chuàng)建的對象之間的這一關(guān)系。
var spot = new Dog(“Spot”);// Dog.prototype is the prototype of spotalert(Dog.prototype.isPrototypeOf(spot)); // 說明spot對象是從原型繼承來的// spot inherits the constructor property// from Dog.prototypealert(spot.constructor == Dog.prototype.constructor);/** 上例說明:* 說明構(gòu)造函數(shù)都有一個(gè)名為"prototype"的屬性,該屬性指向原型,* 而原型又有一個(gè)指向構(gòu)造函數(shù)的屬性"constructor",很顯然這是* 一個(gè)圈(循環(huán)引用)。*/alert(spot.constructor == Dog);/** 上例說明:* 因?yàn)槊總€(gè)對象都繼承自原型,所以每個(gè)對象都從原型中繼承了constructor* 屬性,而這個(gè)屬性指向構(gòu)造函數(shù)。*/// But constructor property doesn’t belong// to spot. The line below displays “false”alert(spot.hasOwnProperty(“constructor”));// The constructor property belongs to Dog.prototype// The line below displays “true”alert(Dog.prototype.hasOwnProperty(“constructor”));/ ** 上面兩個(gè)例子第一個(gè)alert彈出的提示是false,第二個(gè)是true,* 這再次說明了,"constructor"屬性不是對象本身的屬性,* 而是來自于對象的原型。*/
某些讀者可能已經(jīng)注意到圖4中對hasOwnProperty和isPrototypeOf方法的調(diào)用。這些方法是從哪里來的呢〉它們不是來自Dog.prototype。實(shí)際上,在Dog.prototype和Dog實(shí)例中還可以調(diào)用其他方法,比如:toString()、toLocaleString和valueOf,但它們都不來自Dog.prototype。您會(huì)發(fā)現(xiàn),就像.NET Framework中的System.Object充當(dāng)所有類的最終基類一樣,JavaScript中的Object.prototype是所有原型的最終基礎(chǔ)原型。(Object.prototype的原型是null)
在此示例中,請記住Dog.prototype是對象。它是通過調(diào)用Object構(gòu)造函數(shù)創(chuàng)建的(盡管它不可見):
Dog.prototype = new Object();
因此,正如Dog實(shí)例繼承Dog.prototype一樣,Dog.prototype繼承Object.prototype。這使得所有Dog實(shí)例也繼承了Object.prototype的方法和屬性。所以每個(gè)Dog實(shí)例也就繼承了我們上面說的toString、toLocaleString、hasOwnProperty方法了。
每個(gè)JavaScript對象都繼承一個(gè)原型鏈,而所有原型都終止于Object.prototype。注意,迄今為止您看到的這種繼承是活動(dòng)對象之間的繼承。它不同于繼承的常見概念,后者是指在聲明類時(shí)類之間的發(fā)生的繼承,因此,JavaScript繼承動(dòng)態(tài)性更強(qiáng)。它使用簡單算法實(shí)現(xiàn)這一點(diǎn),如下所示:當(dāng)您嘗試訪問對象的屬性/方法時(shí),JavaScript將檢查該屬性/方法是否是在該對象中定義的。如果不是,則檢查對象的原型,如果還不是,則檢查該對象的原型的原型,如此繼續(xù),一直檢查到Object.prototype。圖6說明了此解析過程。
圖 6 在原型鏈中解析 toString() 方法
JavaScript動(dòng)態(tài)地解析屬性訪問和方法調(diào)用的方式產(chǎn)生了一些特殊效果:
圖7說明了這些效果。圖7還顯示了如何解決前面遇到的不需要的方法實(shí)例的問題。通過將方法放在原型內(nèi)部,可以使對象共享方法,而不必使每個(gè)對象都有單獨(dú)的函數(shù)對象實(shí)例。在此示例中,rover和spot共享getBreed方法,直至在spot中以任何方式改寫toString方法。此后,spot有了它自己版本getBreed方法,但rover對象和用新GreatDane創(chuàng)建的后續(xù)對象仍共享在GreatDane.prototype對象中定義的那個(gè)getBreed方法實(shí)例。
Figure 7 繼承原型
聯(lián)系客服