輕鬆學習JavaScript十二:JavaScript基於物件導向之建立對象(一)
這一次我們深入的學習一下JavaScript物件導向技術,在學習之前,必要的說明一下一些物件導向的一些術語。
這也是所有面對對象語言所擁有的共同點。有這樣幾個物件導向術語:
對象
ECMA-262把對象(object)定義為“屬性的無序集合,每個屬性存放一個原始值、對象或函數”。嚴格來說,這意味
著對象是無特定順序的值的數組。儘管ECMAScript如此定義對象,但它更通用的定義是基於代碼的名詞(人、地點或
事物)的表示。
類
每個對象都由類定義,可以把類看做對象的配方。類不僅要定義對象的介面(interface)(開發人員訪問的屬性和方
法),還要定義對象的內部工作(使屬性和方法發揮作用的代碼)。編譯器和解釋程式都根據類的說明構建對象。
執行個體
程式使用類建立對象時,產生的對象叫作類的執行個體(instance)。對類產生的對象的個數的唯一限制來自於運行
代碼的機器的實體記憶體。每個執行個體的行為相同,但執行個體處理一組獨立的資料。由類建立對象執行個體的過程叫做執行個體化
(instantiation)。
在前面的章節我們提到過,ECMAScript並沒有正式的類。相反,ECMA-262把對象定義描述為對象的配方。這
是 ECMAScript邏輯上的一種折中方案,因為對象定義實際上是對象自身。即使類並不真正存在,我們也把對象定義
叫做類,因為大多數開發人員對此術語更熟悉,而且從功能上說,兩者是等價的。
使用預定義對象只是物件導向語言的能力的一部分,它真正強大之處在於能夠建立自己專用的對象。
ECMAScript 擁有很多建立對象的方法。
一原始方式
因為對象的屬性可以在對象建立後動態定義,所有許多開發人員都在JavaScript 最初引入時編寫類似下面的代碼:
var Car = new Object();Car.color = blue;Car.doors = 4;Car.mpg = 25;Car.showColor = function() { return this.color;};document.write(Car.showColor());//輸出:blue
在上面的代碼中,建立對象Car。然後給它設定幾個屬性:它的顏色是藍色,有四個門,每加侖油可以跑 25 英
裡。最後一個屬性實際上是指向函數的指標,意味著該屬性是個方法。執行這段代碼後,就可以使用對象Car。不過
這裡有一個問題,就是可能需要建立多個Car的執行個體,這樣就造成了我們會重複許多類似的代碼,這樣會很麻煩。
二工廠方式
要解上述的多個類似對象聲明的問題,開發人員創造了能建立並返回特定類型的對象的工廠方式。這種方式就是為
瞭解決執行個體化對象產生大量重複的問題。
(1)無參數的工廠方式
例如,函數createCar()可用於封裝前面列出的建立Car對象的操作:
function createCar() {var TempCar = new Object();TempCar.color = blue;TempCar.doors = 4;TempCar.mpg = 25;TempCar.showColor = function() { return this.color; }; return TempCar;};var Car1 = createCar();var Car2 = createCar();document.write(Car1.showColor()+);//輸出:bluedocument.write(Car2.showColor());//輸出:blue
在這裡,第一個例子中的所有代碼都包含在createCar()函數中。此外,還有一行額外的代碼,返回TempCar 對
象作為函數值。調用此函數,將建立新對象,並賦予它所有必要的屬性,複製出一個我們在前面說明過的Car對象。
因此,通過這種方法,我們可以很容易地建立Car對象的兩個版本(Car1和 Car2),它們的屬性完全一樣。
(2)有參數的工廠方式
我們還可以修改createCar()函數,給它傳遞各個屬性的預設值,而不是簡單地賦予屬性預設值:
function createCar(Color,Doors,Mpg) { var TempCar = new Object(); TempCar.color = Color; TempCar.doors = Doors; TempCar.mpg = Mpg; TempCar.showColor = function() { return this.color; }; return TempCar;};var Car1 = createCar(red,4,23);var Car2 = createCar(blue,3,25);document.write(Car1.showColor()+);//輸出:reddocument.write(Car2.showColor());//輸出:blue
給createCar()函數加上參數,即可為要建立的Car對象的color、doors 和mpg屬性賦值。這使兩個對象具有相同
的屬性,卻有不同的屬性值。
工廠方式解決了重複執行個體化的問題,但是還是有一個問題,那就是前面的例子中,每次調用函數createCar(),都
要建立新函數showColor(),意味著每個對象都有自己的 showColor() 版本。而事實上,每個對象都共用同一個函數。
有些開發人員在工廠函數外定義對象的方法,然後通過屬性指向該方法,從而避免這個問題:
function showColor() { return this.color;};function createCar(Color,Doors,Mpg) { var TempCar = new Object(); TempCar.color = Color; TempCar.doors = Doors; TempCar.mpg = Mpg; TempCar.showColor = showColor; return TempCar;};var Car1 = createCar(red,4,23);var Car2 = createCar(blue,3,25);document.write(Car1.showColor()+);//輸出:reddocument.write(Car2.showColor());//輸出:blue
在上面這段重寫的代碼中,在函數 createCar()之前定義了函數 showColor()。在createCar()內部,賦予對象一個
指向已經存在的 showColor() 函數的指標。從功能上講,這樣解決了重複建立函數對象的問題;但是從語義上講,該
函數不太像是對象的方法。所有這些問題都引發了開發人員定義的建構函式的出現。
三建構函式方式
建立建構函式就像建立工廠方式的函數一樣容易。第一步選擇建構函式的名字。根據慣例,這個名字的首字母大
寫,以使它與首字母通常是小寫變數名分開。除了這點不同,建構函式看起來很像工廠方式的函數。請看下面的例
子:
function Car(Color,Doors,Mpg) { this.color = Color; this.doors = Doors; this.mpg = Mpg; this.showColor = function() { return this.color; };};var Car1 = new Car(red,4,23);var Car2 = new Car(blue,3,25);document.write(Car1.showColor()+);//輸出:reddocument.write(Car2.showColor());//輸出:blue
下面為您解釋上面的代碼與工廠方式的差別。首先在建構函式內沒有建立對象,而是使用this關鍵字。使用new運
算符建構函式時,在執行第一行代碼前先建立一個對象,只有用this才能訪問該對象。然後可以直接賦予this屬性,默
認情況下是建構函式的傳回值(不必明確使用 return 運算子)。現在,用new運算子和對象名Car建立對象,就更像
ECMAScript 中一般對象的建立方式了。
就像工廠方式的函數,建構函式會重複產生函數,為每個對象都建立獨立的函數版本。不過,與工廠方式的函數
相似,也可以用外部函數重寫建構函式,同樣地,這麼做語義上無任何意義。這正是下面要講的原型方式的優勢所