JavaScript常用的繼承方式
JavaScript常用繼承方式主要分為(7種):原型鏈繼承、建構函式繼承、組合繼承、原型式繼承、寄生式繼承、寄生組合繼承以及繼承多個物件。
1:原型鏈繼承(核心:將父類的例項作為子類的原型)
基本概念:重寫原型物件,賦予一個新的物件的例項。基本思想就是讓一個原型物件指向另一個父類的例項。
function Super() { //基本資料型別 this.text = 'Hello'; } Super.prototype.getSuperText = function() { return this.text; } function Sub() { this.subText = 'Word'; } Sub.prototype = new Super(); const instance = new Sub(); console.log(instance);
特點:非常純粹的繼承關係,例項是子類的例項,也是父類的例項。父類新增原型方法或屬性,子類都能訪問到。
優點:簡單易於操作
缺點:對引用型別資料操作會互相(多個例項之間)影響
function Super() { //複雜物件,也就是引用型別 this.value = [1, 2, 3, 4]; } Super.prototype.getSuperValue = function() { return this.value; } function Sub() { this.subText = 'Word'; } Sub.prototype = new Super(); const instance1 = new Sub(); const instance2 = new Sub(); instance1.value.push(5); console.log(instance2.value); // (5) [1, 2, 3, 4, 5]
2:建構函式繼承
//定義建構函式 function Super(){ this.value = [1, 2, 3, 4]; } //新增屬性getSuperValue Super.prototype.getSuperValue = function() { return this.value; } //sub每次執行都要重新呼叫 function Sub(){ Super.call(this); } const instance1 = new Sub(); instance1.value.push(5); console.log(instance1.value); // (5) [1, 2, 3, 4, 5] const instance2 = new Sub(); console.log(instance2.value); // (4) [1, 2, 3, 4]
建構函式的特點:(對引用資料型別沒有影響)上面的程式碼輸出instance1是1,2,3,4,5,instance2是1,2,3,4。這是因為sub每次在執行時都是重新呼叫了一個super.call(),而且建構函式在構建物件的過程中,每次都是建立了一個新的object,因此每次呼叫sub都會執行一遍super,每次執行時都會有申請一個新的記憶體空間,所以得到的兩個value值是不一樣互不影響的。
缺點:在整個建構函式的基礎過程中,上面的程式碼並沒有使用proto和prototype的屬性,沒有使用的話那麼原型鏈就沒有接上。所以說,建構函式基礎只能繼承父類的例項屬性和方法,不能繼承原型鏈上的屬性和方法。
3:組合繼承
通過呼叫父類構造,繼承父類的屬性並保留傳參的優點,然後通過將父類例項作為子類原型,實現函式複用。
保留了建構函式繼承與原型鏈繼承的優點。但是執行了兩次Person,屬性重複了。
function Person(name) {
this.name = name;
this.value = ["head", "body", "legs"];
}
Person.prototype.getName = function() {
return this.name;
};
// 建構函式繼承
function Teacher(name, school){
// 執行又一次Person
Person.call(this, name);
this.school = school;
}
// 原型鏈繼承
// 執行一次Person
Teacher.prototype = new Person();
const Eric = new Teacher("Eric",27);
Eric.getName();
// 輸出:Eric
// prototype構造器指回自己
Teacher.prototype.constructor = Teacher;
Teacher.prototype.getSchool = function() {
return this.school;
};
特點:既可以繼承例項屬性和方法,也可以繼承原型屬性和方法。既是子類的例項也是父類的例項,不存在引用屬性共享的問題。可以傳參,函式可複用。
缺點:呼叫兩次父類建構函式,生成了兩份例項。
4:原型式繼承
藉助原型可以基於已有的物件建立新的物件,同時還不必因此建立自定義型別。
原理:(本質)利用一個空物件作為一箇中介。
const lakers = {
name: "lakers",
value: ["Micheal", "Wade", "Kobe"]
};
const lakers1 = Object.create(lakers);
const lakers2 = Object.create(lakers);
lakers1.value.push('Fish');
console.log(lakers);
模擬Object.create()
object.create()原理:用一個函式包裝一個物件,然後返回這個函式的呼叫,這個函式就變成了一個可以隨意添增屬性的例項或物件。
Object.prototype.create = function(obj) {
function Fun() {}
Fun.prototype = obj;
return new Fun();
}
缺點有兩點:第一點是無法傳遞引數,第二點是引用型別存在變數的汙染。****
5:寄生式繼承
寄生式繼承的思路與寄生建構函式和工廠模式類似,即建立一個僅用於封裝繼承過程的函式。
目的:在原型式繼承的基礎上,寄生增加了一些新的方法和屬性。
它的特點同原型式繼承一樣,也是無法傳遞引數,而且引用的資料型別也容易存在樣式汙染。
Object.createNew()
Object.prototype.createNew = function(obj){
var newObj = Object.create(obj);
//獲取長度等於一個function
newObj.getLength = function(){ ... };
return newObj;
}
6:寄生組合繼承
目的:為了解決資料重複拷貝兩遍的問題。
Super只執行一次。
//定義Super建構函式
function Super(name) {
this.name = name;
this.value = ["Hello", "Word"];
}
//在super的原型鏈新增一個getName
Super.prototype.getName = function() {
return this.name;
};
//定義Sub
function Sub(name, age) {
//呼叫建構函式繼承
Super.call(this, name);
this.age = age;
}
let prototype = Object.create(Super.prototype);
prototype.constructor = Sub;
Sub.prototype = prototype;
Sub.prototype.getAge = function(){
return this.age;
}
const instance1 = new Sub("Eric", 23);
const instance2 = new Sub("Vico", 23);
instance1.value.push("!");
instance2.value.push("!!");
7:繼承多個物件
藉助原型式繼承Object.create拿到SuperClass,也就是父類,拿到父類的prototype之後把它賦給ClassOne,
再然後我們將ClassTwo的prototype使用一個Object.assign,一個物件的拷貝,把它拷貝到ClassOne裡面來,
然後最後ClassOne.prototype.constructor等於ClassOne
也就是使用一個Class.assign把所有我們想要繼承的父類的prototype全部組合到一起完成一個拷貝,之後再賦給物件。
function
ClassOne.prototype = Object.create(SuperClass.prototype);
Object.assign(ClassOne.prototype, ClassTwo.prototype);
ClassOne.prototype.constructor