我們知道,JS是物件導向的。談到物件導向,就不可避免的要涉及類的概念。一般像c#,java這些強型別語言都有固定的定義類的文法。而JS的不同之處在於它能使用各種方法實現自己的類和對象。一般的實現有以下幾種方式:
1.工廠方式
工廠方式是指建立一個返回特定物件類型的工廠函數,範例程式碼如下:
複製代碼 代碼如下:
function createCar(sColor,iDoors,iMpg)
{
var oTempCar=new Object;
oTempCar.color=sColor;
oTempCar.doors=iDoors;
oTempCar.mpg=iMpg;
oTempCar.showColor=function()
{
alert(this.color);
}
return oTempCar;
}
var oCar1=createCar("red",4,23);
var oCar2=createCar("blue",3,25);
oCar1.showColor();
oCar2.showColor();
這種方式每次調用它的工廠函數,都會建立一個新對象。可問題在於每次產生一個新對象,都要建立新函數showColor,這使得每個對象都有自己的showColor版本,而事實上,所有的對象都共用同一個函數.為解決這個問題,開發人員在工廠函數的外面定義了對象的方法,然後賦予對象一個指標指向這個這個函數,如下
複製代碼 代碼如下:
function showColor()
{
alert(this.color);
}
function createCar(sColor,iDoors,iMpg)
{
var oTempCar=new Object;
oTempCar.color=sColor;
oTempCar.doors=iDoors;
oTempCar.mpg=iMpg;
oTempCar.showColor=showColor;
return oTempCar;
}
var oCar1=createCar("red",4,23);
var oCar2=createCar("blue",3,25);
oCar1.showColor();
oCar2.showColor();
這樣就不需要為每一個對象都建立自己的showColor函數,而只是建立指向這個函數的指標.這從功能上解決了問題,但是該函數卻不像對象的方法。於是,引出了建構函式的方式。
2.建構函式方式
建構函式與工廠函數很相似,範例程式碼如下:
複製代碼 代碼如下:
function Car(sColor,iDoors,iMpg)
{
this.color=sColor;
this.doors=iDoors;
this.mpg=iMpg;
this.showColor=function()
{
alert(this.color);
}
}
var oCar1=new Car("red",4,23);
var oCar2=new Car("blue",3,25);
在建構函式中,內部無建立對象,而是使用this關鍵字。使用new運算子調用建構函式時,在執行第一行代碼之前先建立一個對象,只有用this才能訪問這個對象。但是這會遇到什麼問題呢,很顯然,它的每個對象也都會建立自己的showColor函數版本。為解決這個問題,引出了以下的原型方式.
3.原型方式
該方式利用了對象的prototype屬性,可把它看成建立新對象所依賴的原型。這裡,用空建構函式來設定類名。然後把所有的方法和屬性都直接賦予prototype屬性。如下:
複製代碼 代碼如下:
function Car()
{}
Car.prototype.color="red";
Car.prototype.doors=4;
Car.prototype.mpg=23;
Car.prototype.drivers=new Array("Mike","Sue");
Car.prototype.showColor=function()
{
alert(this.color);
}
原型方式只能直接賦值,而不能通過給建構函式傳遞參數初始化屬性的值。在用這種方式時,會遇到兩個問題,不知道大家注意到沒有。第一問題是採用這種方式必須建立每個對象後才能改變屬性的預設值。而不能在建立每個對象時都會直接有自己所需要的屬性值。這點很討厭。第二個問題在於屬性所指的是對象的時候。函數共用不會出現任何問題,但是對象共用卻會出現問題。因為每個執行個體一般都要實現自己的對象。
如下面:
複製代碼 代碼如下:
var oCar1=new Car();
var oCar2=new Car();
oCar1.drivers.push("Matt");
alert(oCar1.drivers);//輸出 "Mike,Sue,Matt"
alert(oCar2.drivers);//輸出"Mike,Sue,Matt"
因此drivers屬性只是指向對象的指標,所以所有的執行個體事實上共用同一個對象。由於出現這這些問題,我們引出了下面的聯合使用建構函式和原型方式。
4.混合的建構函式/原型方式
這種方式的思想是用建構函式定義對象的所有非函數屬性(包括普通屬性和指向對象的屬性),用原型方式定義對象的函數屬性(方法)。結果使得所有的函數都只被建立一次,而每個對象都有自己的對象屬性執行個體。範例程式碼如下:
複製代碼 代碼如下:
function Car(sColor,iDoors,iMpg)
{
this.color=sColor;
this.doors=iDoors;
this.mpg=iMpg;
this.drivers=new Array("Mike","Sue");
}
Car.prototype.showColor=function()
{
alert(this.color);
}
var oCar1=new Car("red",4,23);
var oCar2=new Car("blue",3,25);
oCar1.drivers.push("Matt");
alert(oCar1.drivers);//輸出 "Mike,Sue,Matt"
alert(oCar2.drivers);//輸出 "Mike,Sue"
由執行個體代碼可知,這種方式同時解決了上一種方式的兩個問題。不過,採用這種方式,仍有些開發人員覺得不夠完美。
5.動態原型方式
我們可知,大多數物件導向語言都對屬性和方法進行了視覺上的封裝。而上述方式的showColor方法卻定義在了類的外面。因此,他們設計了動態原型方法。這種方式的基本思想和混合的建構函式/原型方式相同,唯一不同之處在於對象方法的位置。如下所示:
複製代碼 代碼如下:
function Car(sColor,iDoors,iMpg)
{
this.color=sColor;
this.doors=iDoors;
this.mpg=iMpg;
this.drivers=new Array("Mike","Sue");
if(typeof Car._initialized=="undefined")
{
Car.prototype.showColor=function()
{
alert(this.color);
}
}
Car._initialized=true;
}
這種方式Car.prototype.showColor只被建立一次。這樣依賴,這段代碼更像其他語言中的類定義了。
6.混合工廠方式
這種方式通常是不能應該前一種方式的變通方法。它的目的是建立假建構函式,只返回另一種對象的新執行個體。
複製代碼 代碼如下:
function createCar()
{
var oTempCar=new Object;
oTempCar.color=“red”;
oTempCar.doors=4;
oTempCar.mpg=23;
oTempCar.showColor=function()
{
alert(this.color);
};
return oTempCar;
}
var car=new Car();
由於在Car()建構函式內部調用了new運算子,所以自動忽略第二個new運算子。在建構函式內部建立的對象被傳遞迴變數var。這種方式在對象方法的內部管理方面與經典方式有著相同的問題。所以強烈建議:除非萬不得已,還是避免使用這種方式。