1. 對象冒充
對象冒失是在函數環境中使用this關鍵字後發展出來的一種繼承方式。其原理如下:構造函數使用this關鍵字給所有屬性和方法指派(即采用類聲明的構造函數方式)。因為構造函數隻是一個函數,是以可使classa的構造函數成為classb的方法,然後調用它。classb就會收到classa的構造函數中定義的屬性和方法。例如,用下面的方式定義classa和classb:
function classa (scolor) {
this.color = scolor;
this.saycolor = funciton() {
alert(this.scolor);
};
}
function classb (scolor) {
還記得嗎,關鍵字this引用的是構造函數目前建立的對象,不過在這個方法中,this指向的是所屬的對象。這個原理是把classa作為正常函數來建立繼承機制,而不是作為構造函數。如下使用構造函數classb可以實作繼承機制:
this.newmethod = classa;
this.newmethod(scolor);
delete this.newmethod;
在這段代碼中,位classa賦予了方法newmethod。然後調用該方法,傳遞給它的是classb的構造參數scolor。最後一行代碼删除了對classa的引用,這樣以後就不能在調用它。所有的新屬性和新方法都必須在删除了新方法的代碼行後定義,否則,可能會覆寫超類的相關屬性和方法。
有意思的是,對象冒充可以支援多重繼承,也就是說,一個類可以繼承多個超類。如,如果存在兩個類classx和classy,classz想繼承這兩個類,可以使用下面的代碼:
function classz {
this.newmethod = classx;
this.newmethod();
delete this.newmethod;
this.newmethod = classy;
}
不過,這裡存在一個弊端,如果classx和classy具有同名的屬性或方法,classy具有高優先級,因為它從後面的類繼承。
2. call()方法
call()方法是與經典的對象冒充方法最相似的方法。它的第一個參數用作this的對象,其他參數都直接傳遞給函數自身。例如:
function saycolor(sprefix, ssuffix) {
alert(sprefix + this.color + ssuffix);
}
var obj = new object();
obj.color = "red";
saycolor.call(obj, "color is", ", a nice color"); // output: color is red, a nice color
在這個例子中,函數saycolor()在對象外定義,即使它不屬于任何對象,也可以應用關鍵字this。對象obj的color屬性等于"red"。調用call()方法時,第一個參數是obj,說明應該賦予saycolor()函數中的this關鍵字值是obj。第二個和第三個參數是字元串。它們與saycolor()函數中的參數sprefix和ssuffix比對。
要與繼承機制的對象冒充方法一起使用該方法,隻需将前三行的指派、調用和删除代碼替換即可:
function classb(scolor, sname) {
//this.newmethod = classa;
//this.newmethod();
//delete this.newmethod;
classa.call(this, scolor);
this.name = sname;
this.sayname = function() {
alert(this.name);
};
這裡,想讓classa中的關鍵字this等于新建立的classb對象,是以this是第一個參數。第二個參數scolor對兩個類來說都是唯一的參數。
3. apply()方法
apply()方法有兩個參數,用作this的對象和要傳遞給函數的參數的數組。如:
saycolor.apply(obj, new array("the color is ", ", a nice color"));
這個例子與前面的例子相同,隻是現在調用的是apply()方法。調用apply()方法時,第一個參數仍是obj,說明應賦予saycolor()中的this關鍵字值obj。第二個參數是由兩個字元串構成的數組,與saycolor()的參數sprefix和ssuffix比對。
該方法也用于替換前三行的指派、調用和删除新方法的代碼:
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);
};
}
同樣的,第一個參數仍是this。第二個參數是隻有一個值color的數組。可以把classb的整個arguments對象作為第二個參數傳遞給apply()方法:
function classb(scolor, sname) {
//this.newmethod = classa;
//this.newmethod(scolor);
//delete this.newmethod;
classa.apply(this, arguments);
this.name = sname;
this.sayname = function() {
alert(this.name);
};
}
當然,隻有超類中的參數順序與子類中的參數順序完全一緻時才可以傳遞參數對象。如果不一緻,就必須建立一個單獨的數組,按照正确的順序放置參數。
4. 原型鍊
我們知道,prototype對象其實是個模闆,要執行個體化的對象都以這個模闆為基礎,prototype對象的任何屬性和方法都被傳遞給那個類的所有執行個體。原型鍊利用這種功能來實作繼承機制。
如果用原型方式重定義前面的例子中的類,它們将變為下列形式:
function classa() {
classa.prototype.color = "red";
classa.prototype.saycolor = function() {
alert(this.color);
};
function classb() {
classb.prototype = new classa();
這裡,把classb的prototype屬性設定成classa的執行個體,這樣就可以得到classa的所有屬性和方法。
注意:調用classa的構造函數時,沒有給它傳遞參數。這在原型鍊中時标準做法。要確定構造函數沒有任何參數。
與對象冒充相似,子類的所有屬性和方法都必須出現在prototype屬性被指派後,因為在它之前指派的所有方法都會被删除,因為prototype屬性被替換成了新對象,添加了新方法的原始對象将被銷毀。是以,為classb類添加name屬性和sayname()方法的代碼如下:
function classb() {
classb.prototype = new classa();
classb.prototype.name = "";
classb.prototype.sayname = function() {
alert(this.name);
};
此外,在原型鍊中,instanceof運算符的運作方式也很獨特。對classb的所有執行個體,instanceof為classa和classb都傳回true。
現在我們有了四種繼承的方法,其中,用對象冒充繼承構造函數的屬性,用原型鍊繼承prototype對象的方法。用這兩種方式重寫前面的例子,代碼如下:
function classa(scolor) {
this.color = scolor;
alert(this.color);
function classb(scolor, sname) {
classa.call(this, scolor);
this.name = sname;
classb.prototype.sayname = function() {
alert(this.name);