關于javascript中類的繼承可以參考阮一峰的Blog《Javascript繼承機制的設計思想》,說的很透。
一、在javascript中實例化遇到的問題:
下面用《javascript高級程序設計》中的例子來做說明,假如現在定義了一個car的對象,它是Object類的實例。像下面這樣的:
var oCar=new Object();
oCar.color = "red";
oCar.doors = 4;
oCar.mpg = 23;
oCar.showColor = function () {
alert(this.color);
};
現在又需要這樣的一個實例,你可能會像這樣來定義:
var oCar2 = new Object();
oCar2.color = "blue";
oCar2.doors = 5;
oCar2.mpg = 25;
oCar2.showColor = function () {
alert(this.color);
};
這樣遇到的問題是每個對象都需要重新定義一次他的字段和方法。很麻煩。
二、類的定義--工廠方式實現:
對上面的例子進行一個包裝,利用函數的返回值來做文章:
function createCar() {
var oTempCar = new Object();
oTempCar.color = "red";
oTempCar.doors = 4;
oTempCar.mpg = 23;
oTempCar.showColor = function () {
alert(this.color);
};
return oTempCar;
}
調用方式:
var oCar1 = createCar();
var oCar2 = createCar();
這種方式被稱之為工廠方式。工廠方式看起來是省事多了。起碼創建一個對象的時候不再需要那么多的行數。因為每個屬性(color,doors,mpg)的值都是固定的,還需要再次進行改造,利用參數傳遞來實現:
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("red", 4, 23);
oCar1.showColor();
oCar2.showColor();
這樣做看似的確可以實現了對象了。實現也很簡單,調用也很方便。但是有兩個不是很好的地方:
1、從語義上看,在創建對象時沒有使用new運算符,似乎不是那么正規(通常創建一個對象都用一個new運算符來實現)。
2、不符合面向對象的特征--封裝。在這個例子中,oCar1和oCar2都有自己的showColor方法,并且他們的showColor都是自己的實現。但是事實是他們共享的是同一個函數。
也是有辦法解決這個共享函數的問題,利用函數指針來解決。在createCar函數之外再創建一個showColor函數,而oTempCar的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("red", 4, 23);
oCar1.showColor();
oCar2.showColor();
雖然這樣解決了重復創建函數的問題,但這樣的話,就使showColor函數看起來不像是對象的方法。
三、類的定義--構造函數方式實現:
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("red", 4, 23);
oCar1.showColor();
oCar2.showColor();
在Car類中,this指針代表了Car的一個實例,因此不需要返回值。雖然構造函數方式實現了類的定義,但是和工廠方式一樣,他也是為每個實例創建一個單獨的方法。雖然可以像工廠函數一樣在函數之外再創建一個函數利用指針來解決這個問題,但是這么做的話,在語義上沒有意義。
四、類的定義--原型方式實現:
利用對象的prototype屬性,把它看成是創建新對象所依賴的原型。用空構造函數來設置類名。然后所有的屬性和方法都被直接賦予prototype屬性。
function Car() {
}
Car.prototype.color = "red";
Car.prototype.doors = 4;
Car.prototype.mpg = 23;
Car.prototype.showColor = function () {
alert(this.color);
};
var oCar1 = new Car();
var oCar2 = new Car();
alert(oCar1 instanceof Car);//output true這里存在兩個問題:
1、構造函數沒有參數。使用原型時,不能通過給函數參數傳遞參數來初始化屬性值。
2、在有多個實例時,對其中一個實例的屬性的更改會影響到另外一個實例的屬性。
測試代碼:
var oCar1 = new Car();
oCar1.color = "Green";
var oCar2 = new Car();
oCar2.color = "Black";
alert(oCar1.color); //output Green
alert(oCar2.color); //output Black
alert(oCar1.color); //output Black
當然了,也會有辦法解決這個問題的。那就是混合的構造函數/原型方式
五、類的實現--混合的構造函數/原型方式實現
這種實現方式是將每個類的實例中共享的屬性或者方法妨到原型鏈中實現,而將不需要共享實現的屬性和方法放在構造函數中實現。這中類的實現方式是使用最廣泛的方式。
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, 24);
oCar1.drivers.push("Matt");
alert(oCar1.drivers);
alert(oCar2.drivers);六、類的定義--動態原型方式實現
這種方式和混合的構造函數/原型方式相比,提供了一種友好的編程風格(在混合的構造函數/原型方式中,showColor方法的定義是在方法體外實現的,而不是在構造函數的方法體內完成的)。這種類的定義方式使用也很多。
function Car(sColor, iDoors, iMpg) {
this.color = sColor;
this.doors = iDoors;
this.mpg = iMpg;
this.divers = new Array("Mike", "Sue");
if (typeof Car._initialized == "undefined") {
Car.prototype.showColor = function () {
alert(this.color);
};
Car._initialized = true;
}
七、類的定義--混合工廠方式實現
function Car() {
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.showColor();
這種方式和工廠方式看起來差不多。由于在Car()構造函數內部調用了new運算符,所以將忽略掉位于構造函數之外的new運算符。在構造函數內部創建的對象被傳回變量var。雖然看起來有了new運算符了,比工廠方式有了一些進步,但是這種實現方式也是會出現重復創建方法的問題。因此也不推薦使用這種方式來定義類。