還是《Professional JavaScript for Web Developers》。
JavaScript也可以對象繼承?當我看到這一章第一個反應便是這個,以前從來沒有想過的,呵呵。
JS實現繼承有如下幾種辦法:
1. 對象冒充
function ClassA(sColor) {
this.color = sColor;
this.sayColor = function() {
alert(this.color);
};
}
function ClassB(sColor, sName) {
this.newMethod = ClassA;
this.newMethod(sColor);
delete this.newMethod;
this.name = sName;
this.sayName = function() {
alert(this.name);
};
}
var objA = new ClassA("red");
var objB = new ClassB("blue", "Nicholas");
objA.sayColor(); //outputs "red"
objB.sayColor(); //outputs "blue"
objB.sayName(); //outputs "Nicholas"
呵呵,是不是很有趣。注意黃色代碼,所有新的屬性和新的方法必須在刪除了newMethod的代碼行后定義。否則,可能會覆蓋超類的相關屬性和方法。
然后。。。用這個方法,還可以實現多重繼承,哈哈哈
function ClassZ() {
this.newMethod = ClassX;
this.newMethod();
delete this.newMethod;
this.newMethod = ClassY;
this.newMethod();
delete this.newMethod;
}
不過這里有個小弊端,就是如果ClassX和ClassY有同名的屬性和方法的話,ClassY具有優先級,使用時要注意點,呵呵。
2. call()方法
先看看call()方法的使用:
function sayColor(sPrefix, sSuffix) {
alert(sPrefix + this.color + sSuffix);
};
var obj = new Object();
obj.color = "red";
sayColor.call(obj, "The color is ", ", a very nice color indeed.");
這個例子中,sayColor雖然在對象外定義,即使他不屬于任何對象,也可以引用關鍵字this。調用call()方法時,第一個參數是obj,說明應該賦予sayColor()函數中的參數的this關鍵字值是obj,第二個和第三個參數就是sayColor()函數本身的參數sPrefix和sSuffix
要與冒充對象方法一起使用該方法,只需要將前三行的賦值、調用和刪除代碼替換即可:
function ClassA(sColor) {
this.color = sColor;
this.sayColor = function() {
alert(this.color);
};
}
function ClassB(sColor, sName) {
//this.newMethod = ClassA;
//this.newMethod(sColor);
//delete this.newMethod;
ClassA.call(this, sColor);
this.name = sName;
this.sayName = function() {
alert(this.name);
};
}
var objA = new ClassA("red");
var objB = new ClassB("blue", "Nicholas");
objA.sayColor(); //outputs "red"
objB.sayColor(); //outputs "blue"
objB.sayName(); //outputs "Nicholas"
3. apply()方法
apply()方法和call()方法很相似,唯一不同的就是將call()方法后面帶的多個參數存入數組再進行傳遞:
function sayColor(sPrefix, sSuffix) {
alert(sPrefix + this.color + sSuffix);
};
var obj = new Object();
obj.color = "red";
sayColor.apply(obj, new Array("The color is ", ", a very nice color indeed."));
自然,很容易得出用apply()方法實現繼承的代碼:
function ClassA(sColor) {
this.color = sColor;
this.sayColor = function() {
alert(this.color);
};
}
function ClassB(sColor, sName) {
//this.newMethod = ClassA;
//this.newMethod(sColor);
//delete this.newMethod;
ClassA.apply(this, new Array(sColor));
this.name = sName;
this.sayName = function() {
alert(this.name);
};
}
var objA = new ClassA("red");
var objB = new ClassB("blue", "Nicholas");
objA.sayColor(); //outputs "red"
objB.sayColor(); //outputs "blue"
objB.sayName(); //outputs "Nicholas"
其中,如果超類參數順序與子類相同,圖中黃色區域可以這么寫:
ClassA.apply(this, arguments);
4. 原型鏈
function ClassA() {
}
ClassA.prototype.color = "red";
ClassA.prototype.sayColor = function() {
alert(this.color);
};
function ClassB() {
}
ClassB.prototype = new ClassA();
ClassB.prototype.name = "Nichloas";
ClassB.prototype.sayName = function() {
alert(this.name);
}
這個。。黃色那一句還是比較神奇的,呵呵。
不過要注意,調用ClassA的構造函數時,沒有給它傳遞參數,這在原型鏈中是標準做法,要確保構造函數沒有任何參數!
同時要注意上面的代碼,和對象冒充類似,子類的所有的屬性和方法都必須出現在prototype屬性被賦值后,因為在它之前賦值的所有方法都會被刪除!
原型鏈的好處在于可以使用類似如下代碼檢測:
var objB = new ClassB();
alert(objB instanceof ClassA) //outputs "true"
alert(objB instance of ClassB) //outputs "true"
而它的壞處在于不能多重繼承,不過用慣Java的人應該比較習慣吧,呵呵。
5. 混合方式
總結以上幾種方式,對象冒充的問題是必須使用構造函數方式,這不是最好的選擇(參考我的另一片讀書筆記《讀書筆記之JavaScript的類編寫方法》)。但如果使用原型鏈,又無法使用帶參數的構造函數了。那么混合模式就是解決這兩個問題的最好答案了,呵呵:
function ClassA(sColor) {
this.color = sColor;
}
ClassA.prototype.sayColor = function() {
alert(this.color);
};
function ClassB(sColor, sName) {
ClassA.call(this, sColor);
this.name = sName;
}
ClassB.prototype = new ClassA();
ClassB.prototype.sayName = function() {
alert(this.name);
};
這種方式是推薦使用的,呵呵。
最后還是那句話,以上源代碼均來自Nicholas C. Zakas的《Professional JavaScript for Web Developers》