js繼承機制的實現 1. 繼承的概念 1. 說明繼承的最經典的例子:幾何形狀。實際上,幾何形狀只有兩種,即橢圓形(是圓形的)和多邊形(具有一定數量的邊)。圓是橢圓的一種,它只有一個焦點。三角形、矩形和五邊形都是多邊形的一種,具有不同數量的邊。正方形是矩形的一種,所有的邊等長。這就構成了一種完美的繼承 ...
js繼承機制的實現
1. 繼承的概念
- 說明繼承的最經典的例子:幾何形狀。實際上,幾何形狀只有兩種,即橢圓形(是圓形的)和多邊形(具有一定數量的邊)。圓是橢圓的一種,它只有一個焦點。三角形、矩形和五邊形都是多邊形的一種,具有不同數量的邊。正方形是矩形的一種,所有的邊等長。這就構成了一種完美的繼承關係。
- 在該實例中,我想闡述的並不是繼承的含義,而是說明幾個關鍵詞,即:基類 子類 超類 的含義。
在該實例中,圓形是橢圓形的子類,橢圓形是圓形的超類(父類);同樣,三角形(Triangle)、矩形(Rectangle)和五邊形(Pentagon)都是多邊形的子類,多邊形是它們的超類。
***2. 繼承的方式
- 對象冒充
// 父類 ClassA
function ClassA(aColor){
this.color = aColor;
this.showColor = function(){
console.log(this.color);
}
}
// 子類 ClassB
function ClassB(bColor,bName){
this.newMethod = ClassA; // 函數名ClassA只是指向函數的指針,所以這裡是將this.newMethod也指向ClassA這個函數,所以函數ClassB就擁有了函數ClassA的方法和屬性
this.newMethod(bColor);
delete this.newMethod; // 該操作使得實例化的對象不需要多次繼承同一個函數了,因為繼承一次就可以了。註意:新增的屬性和方法最好都寫在 刪除對另一個函數引用的後面,因為如果在前面定義的話,如果父類剛好有該屬性或者方法你就會把父類的屬性和方法 給覆蓋掉。
this.name = bName;
this.sayName = function(){
console.log(this.name);
}
}
// --------------------------------------
var objA = new ClassA("blue");
var objB = new ClassB("red", "John");
objA.showColor(); // blue
objB.showColor(); // red
objB.sayName(); // John
- 對象冒充還可以實現多重繼承
例如,如果存在兩個類 ClassX 和 ClassY,ClassZ 想繼承這兩個類,可以使用下麵的代碼:
function ClassZ() {
this.newMethod = ClassX;
this.newMethod();
delete this.newMethod;
this.newMethod = ClassY;
this.newMethod();
delete this.newMethod;
}
這裡存在一個弊端,如果存在兩個類 ClassX 和 ClassY 具有同名的屬性或方法,ClassY 具有高優先順序。因為它從後面的類繼承。除這點小問題之外,用對象冒充實現多重繼承機制輕而易舉。
- call、apply方法(和對象冒充類似)
call、apply這兩個方法的使用方法和原理在上一個博客寫了,可以去參考一下
// call方法
function ClassA(acolor){
this.color = acolor;
this.showColor = function(){
console.log(this.color);
}
}
function ClassB(bcolor){
ClassA.call(this,bcolor); // 這裡的this指的是ClassB實例化的對象,讓this調用函數ClassA,達到繼承效果。第二個參數是對應ClassA的參數
}
var objA = new ClassA('blue');
var objB = new ClassB('red');
objA.showColor(); // blue
objB.showColor(); // red
function ClassA(acolor){
this.color = acolor;
this.showColor = function(){
console.log(this.color);
}
}
function ClassB(bcolor){
// ClassA.apply(this,Array(bcolor));
ClassA.apply(this, arguments); // 這裡使用上面的或者arguments都可以,arguments代表的是實參的類數組對象
}
var objA = new ClassA('blue');
var objB = new ClassB('red');
objA.showColor(); // blue
objB.showColor(); // red
- 原型鏈的方式
function ClassA() {}
ClassA.prototype.color = "blue";
ClassA.prototype.sayColor = function () {
alert(this.color);
};
function ClassB() {}
ClassB.prototype = new ClassA(); // 這是關鍵
原型方式的神奇之處在於“ClassB.prototype = new ClassA()”代碼行。這裡,把 ClassB 的 prototype 屬性設置成 ClassA 的實例。這很有意思,因為想要 ClassA 的所有屬性和方法,但又不想逐個將它們 ClassB 的 prototype 屬性進行手動鏈接。還有比把 ClassA 的實例賦予 prototype 屬性更好的方法嗎?
與對象冒充相似,子類的所有屬性和方法都必須出現在 prototype 屬性被賦值後,因為在它之前賦值的所有方法都會被刪除。為什麼?因為 prototype 屬性被替換成了新對象,添加了新方法的原始對象將被銷毀。所以,為 ClassB 類添加 name 屬性和 sayName() 方法的代碼如下:
function ClassB() {
}
ClassB.prototype = new ClassA();
ClassB.prototype.name = "zjy";
ClassB.prototype.sayName = function () {
alert(this.name);
};
測試代碼如下:
var objA = new ClassA();
var objB = new ClassB();
objA.color = "blue";
objB.color = "red";
objB.name = "John";
objA.sayColor(); // blue
objB.sayColor(); // red
objB.sayName(); // John
- 原型鏈和對象冒充混合使用
用對象冒充的方法繼承構造函數的屬性,再用原型鏈的方式繼承構造函數的方法。
// 父類
function ClassA(acolor){// 之所以方法通過原型鏈來定義是因為方法多數都是對象共用的,如果放在構造函數內部的話,多個實例化對象就會產生多個同樣的方法,所以為了減少這樣的浪費,將方法通過原型鏈定義,這樣即滿足了共用的原則也實現了重覆利用不會浪費。不管在內部定義方法還是通過原型鏈定義,都可以通過其他構造函數的原型鏈繼承到。
this.color = acolor;
}
ClassA.prototype.showColor = function(){ //屬性在構造函數內定義,方法通過原型定義
console.log(this.color);
}
// 子類
function ClassB(bcolor,name){
ClassA.call(this,bcolor); // 通過call()對象冒充的方式繼承構造函數的屬性
this.name = name;
}
ClassB.prototype = new ClassA(); // 通過原型鏈的方式繼承構造函數的方法
ClassB.prototype.showName = function(){
console.log(this.name);
}
測試代碼如下:
var objA = new ClassA('blue');
var objB = new ClassB('red','tom');
objA.showColor(); // blue
objB.showColor(); // red
objB.showName(); // tom