在Javascript中,全部開發者定義的類均可以做爲基類,但出於安全性考慮,本地類和宿主類不能做爲基類,這樣能夠防止公用訪問編譯過的瀏覽器級的代碼,由於這些代碼能夠被用於惡意攻擊。數組
選定基類後,就能夠建立它的子類了。是否使用基類徹底由你決定。有時,你可能想建立一個不能直接使用的基類,它只是用於給子類提供通用的函數。在這種狀況下,基類被看做抽象類。瀏覽器
建立的子類將繼承超類的全部屬性和方法,包括構造函數及方法的實現。在Javascript中,全部方法和屬性都是公用的,所以子類可直接訪問這些方法。子類還可添加超類中沒有的新屬性和方法,也能夠覆蓋超類中的屬性和方法。安全
這裏列出了四種方法來實現繼承機制app
1. 對象冒充函數
對象冒失是在函數環境中使用this關鍵字後發展出來的一種繼承方式。其原理以下:構造函數使用this關鍵字給全部屬性和方法賦值(即採用類聲明的構造函數方式)。由於構造函數只是一個函數,因此可以使ClassA的構造函數成爲ClassB的方法,而後調用它。ClassB就會收到ClassA的構造函數中定義的屬性和方法。例如,用下面的方式定義ClassA和ClassB:this
function ClassA (sColor) {prototype
this.color = sColor;對象
this.sayColor = funciton() {繼承
alert(this.sColor);ip
};
}
function ClassB (sColor) {
}
還記得嗎,關鍵字this引用的是構造函數當前建立的對象,不過在這個方法中,this指向的是所屬的對象。這個原理是把ClassA做爲常規函數來創建繼承機制,而不是做爲構造函數。以下使用構造函數ClassB能夠實現繼承機制:
function ClassB (sColor) {
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;
this.newMethod();
delete this.newMethod;
}
不過,這裏存在一個弊端,若是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的對象和要傳遞給函數的參數的數組。如:
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 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;
}
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);
};