JavaScript實現繼承的幾種方法

JavaScript實現繼承的幾種方法

原型鏈繼承

prototype原型相當於一個對象的父類,所有實例都會共享原型上的屬性和方法。
constructor構造函數相當於一個創建對象的函數,同時也是對象實例的標識符。bigApple,smallApple使用Apple作爲構造函數,初始化就會使用Apple方法;同時這兩個實例對象也屬於Apple類。

// 原型鏈繼承
// fruit水果類
function Fruit() { 
    this.name = "水果";
    this.color = ["green"];
}
Fruit.prototype.sayName = function () { 
    console.log(this.name);
}
function Apple() {  };
// 繼承
Apple.prototype = new Fruit();  // 設置對象原型爲Fruit
Apple.prototype.constructor = Apple;  // 將構造函數改爲Apple
var bigApple = new Apple();
var smallApple = new Apple();
bigApple.sayName();  // 水果
console.log(smallApple.name);  // 水果

原理:重寫子類的原型對象,將父類的方法和屬性繼承到子類上
具體原型鏈如下:

對比沒有繼承前和繼承後的結果

缺點:

1.繼承的Fruit實例會被所有Apple實例(bigApple,smallApple)所共享,如果原型上有引用類型(數組,對象),一旦修改,所有Apple實例的原型都會修改

bigApple.color.push("red");
console.log(bigApple.color);    // ["green", "red"]
console.log(smallApple.color);  // ["green", "red"]

2.創建子類實例(smallApple,bigApple)時,不能向父類傳參。也就是說,子類初始化不能定製父類的屬性和方法,父類對象在繼承時,已經創建。每個子類實例的原型都一樣。

借用構造函數繼承

在子類構造函數內容調用父內構造函數方法

// 借用構造函數繼承
function Fruit(name) { 
    this.name = name;
    this.color = ["green"];
}
Fruit.prototype.sayName = function () { 
    console.log(this.name);
}
function Apple(name) { 
    Fruit.call(this,name);  
};
const bigApple = new Apple('big');
const smallApple = new Apple('small');
bigApple.color.push("red");
console.log(bigApple.color);  // ["green", "red"]
console.log(smallApple.color);  // ["green"]
console.log(bigApple.sayName); //undefined

修改Fruit構造函數的this指向,將其指向Apple的實例對象,給Apple實例初始化
原理:改變父類函數的this指向
優點:實例的屬性不會共享,子類構造函數可以傳遞參數給父類
缺點:父類定義的方法不能被子類繼承下來

組合繼承(終點)

爲了解決上述,父類的方法不能被繼承下來的缺點

// 借用構造函數繼承
function Fruit(name) { 
    this.name = name;
    this.color = ["green"];
}
Fruit.prototype.sayName = function () { 
    console.log(this.name);
}
function Apple(name) { 
    Fruit.call(this,name);  
};
Apple.prototype = new Fruit();
Apple.prototype.constructor = Apple;

const bigApple = new Apple('big');
const smallApple = new Apple('small');
bigApple.color.push("red");
console.log(bigApple.color);  // ["green", "red"]
console.log(smallApple.color);  // ["green"]
bigApple.sayName();  //big

只要子類原型對象設置爲父類實例時,子類就可以繼承父類原型上的方法。
注意:修改子類原型時,必須將原型上構造函數的指向子類對象。否則實例化子類對象時,會用父類的Fruit方法去構造對象;而不是Apple方法。
優點:解決了子類繼承父類方法的問題
缺點:調用兩次父類構造函數方法(Fruit.call(this,name);new Fruit()

寄生組合式繼承(重點)

針對上面連詞使用父類構造函數方法改進
可以使用Object.create()方法替代

function Fruit(name) { 
    this.name = name;
    this.color = ["green"];
}
Fruit.prototype.sayName = function () { 
    console.log(this.name);
}
function Apple(name) { 
    Fruit.call(this,name); 
};
Apple.prototype = Object.create(Fruit.prototype);
Apple.prototype.constructor = Apple;

const bigApple = new Apple('big');
const smallApple = new Apple('small');

Object.create()方法,創建一個新對象,將傳入的對象Fruit.prototype作爲父類
原理:將子類的原型對象 指向 父類的原型對象

多重繼承

理論上js不允許一個對象繼承多個對象
Object.assign(); 可以將參數二對象上的屬性和方法 複製給 參數一對象

function Fruit(name) { 
    this.name = name;
    this.color = ["green"];
}
Fruit.prototype.sayName = function () { 
    console.log(this.name);
}
function Plant(){
    this.desc = "植物類"
}
Plant.prototype.sayHi = function () { 
    console.log("hi");
}
function Apple(name) { 
    Fruit.call(this,name);  
    Plant.call(this);
};
Apple.prototype = Object.create(Fruit.prototype);
// 將植物類原型上的屬性和方法複製給Apple原型
Object.assign(Apple.prototype,Plant.prototype);
Apple.prototype.constructor = Apple;

const bigApple = new Apple('big');
bigApple.sayName();  // big
bigApple.sayHi();   // hi
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章