1、工廠方式
//類的定義
function createCar(){
var oTempCar = new Object;
oTempCar.color = 'red';
oTempCar.doors = 4;
oTempCar.mpg = 23; //時速
oTempCar.showColor = function(){
alert(this.color);
};
return oTempCar;
}
//對象創建
var car1 = new createCar();
var car2 = new createCar();
//也可以定義帶參數的類
function createCar(iColor, iDoors, iMpg){
var oTempCar = new Object;
oTempCar.color = iColor;
oTempCar.doors = iDoors;
oTempCar.mpg = iMpg; //時速
oTempCar.showColor = function(){
alert(this.color);
};
return oTempCar;
}
//對象創建
var car1 = new createCar('red', 4, 23);
var car2 = new createCar('blue', 3, 25);
前面的例子中,每次調用函數createCar(),都要闖建新函數showColor(),意味着每個對象都有自己的showColor()版本,事實上,每個對象都共享了同一個函數。爲了避開此問題,可以採用在工廠函數外定義對象的方法;
function showColor(){
alert(this.color);
};
function createCar(iColor, iDoors, iMpg){
var oTempCar = new Object;
oTempCar.color = iColor;
oTempCar.doors = iDoors;
oTempCar.mpg = iMpg; //時速
oTempCar.showColor = showColor;
return oTempCar;
}
var car1 = new createCar('red', 4, 23);
var car2 = new createCar('blue', 3, 25);
car1.showColor(); //outputs 'red'
car2.showColor(); //outputs 'blue'
儘管上述方法從功能上解決了多次創建函數的問題,但該函數看起來不像是對象的方法。所有這些問題引發了開發者定義的構造函數的出現。
2、構造函數方式
創建構造函數第一步選擇類名,即構造函數的名字,這個名字的首字母大寫。以使它與字母通常是小寫的變量名區分開。
function Car(iColor, iDoors, iMpg){
this.color = iColor;
this.doors = iDoors;
this.mpg = iMpg;
this.showColor = function(){
alert(this.color);
};
}
var car1 = new createCar('red', 4, 23);
var car2 = new createCar('blue', 3, 25);
可以看出其與工廠方式存在以下區別:
a.構造函數內部無創建對象,而是使用this關鍵字。
b.使用new運算符調用構造函數是,在執行第一行代碼前先創建一個對象,只有用this才能訪問該對象。然後可以直接賦予this屬性,默認情況下是構造函數的返回值。但是構造函數方式與工廠方式一樣,會重複生成函數,爲每個對象都創建獨立的函數版本。
3、原型方式
該方式利用了對象的prototype對象,可把它看成創建新對象所依賴的原型。
這裏用空構造函數來設置類名,然後所有的屬性和方法都沒直接賦予prototype屬性。重寫前面的例子代碼如下:
function Car(){}
Car.prototype.color = 'red';
Car.prototype.doors = 4;
Car.prototype.mpg = 23;
Car.prototype.showColor = function(){
alert(this.color);
};
var car1 = new Car();
var car2 = new Car();
在這段代碼中,首先定義構造函數Car(),接下來的幾行代碼,通過給Car的prototype屬性添加屬性去定義Car對象的屬性。調用new Car()是,原型的所有屬性都被立即賦予要創建的對象,意味着所有Car實例存放的都是指向showColor()函數的指針。從語義上將,所有屬性看起來都屬於一個對象,以此解決了前面兩種方式存在的問題。此外,使用該方法,還能instanceof運算符檢查給定變量指向的對象的類型。因此,下面的代碼將輸出true:alert(car1 instanceof Car); //outputs 'true'看起來是個非常好的解決方案。遺憾的是,它並不盡如人意。首先,這個構造函數沒有參數。使用原型方式時不能通過構造函數傳遞參數初始化對象屬性。真正的問題並不在這裏,而是出現在屬性指向的是對象,而不是函數時。函數共享是不會出現問題的,但對象卻很少是被多個實例共享的。
例如:
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 car1 = new Car();
var car2 = new Car();
car1.drivers.push('Mart');
alert(car1.drivers); //outputs 'Mike, Sue,Mart'
alert(car2.drivers); //outputs 'Mike, Sue,Mart'
這裏,屬性drivers是指向Array對象的指針,該數組中包含兩個名字'Mike'和'Sue'。由於drivers是引用值,Car的兩個實例都指向同一個數組。這意味着car1.drivers添加值'Matt',在car2.drivers中也能看到。輸出這兩個指針中的任何一個,結果都是現實字符串'Mike, Sue,Mart'。那麼該如何解決以上提出的問題呢?
4、混合的構造函數/原型方式
用構造函數定義對象的所有非函數屬性,用原型方式定義對象的函數屬性(方法),所有函數都只創建一次,而每個對象都具有自己的對象屬性實例。
function Car(iColor, iDoors, iMpg){
this.color = iColor;
this.doors = iDoors;
this.mpg = iMpg;
this.drivers = new Array('Mike', 'Sue');
}
Car.prototype.showColor = function(){
alert(this.color);
};
var car1 = new createCar('red', 4, 23);
var car2 = new createCar('blue', 3, 25);
car1.drivers.push('Mart');
alert(car1.drivers); //outputs 'Mike, Sue,Mart'
alert(car2.drivers); //outputs 'Mike, Sue'
所有的非函數屬性都在構造函數中創建,意味着又可以用構造函數的參數賦予屬性默認值了。
5、動態原型方式
動態原型方法的基本想法與混合的構造函數/原型方式相同,即在構造函數內部室非函數屬性,而函數屬性則利用原型屬性定義。唯一的區別是賦予對象方法的位置。
function Car(iColor, iDoors, iMpg){
this.color = iColor;
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;
}
}
簡而言之,該方法使用標誌(_initialized)來判斷是否已給原型賦予了任何方法。該方法只創建並賦值一次,更符合其他語言中類的定義了。
6、混合工廠方式
它的目的是創建假構造函數,只返回另一種對象的新實例。
function Car(){
var oTempCar=new Object;
oTempCar.color=”red”;
oTempCar.doors=4;
oTempCar.mpg=23;
oTempCar.showColor=function(){
alert(this.color);
};
return oTempCar;
}
由於在Car()構造函數內部調用了new運算符,所以將忽略第二個new運算符(位於構造函數之外)。在構造函數內部創建的對象被傳遞迴變量var。