原型鏈繼承
原型鏈繼承是ECMAScript的主要繼承方式,其基本思想就是通過原型繼承多個參考型別的屬性和方法,什么是原型鏈?每個建構式都會有一個原型物件,呼叫建構式創建的實體會有一個指標__proto__指向原型物件,這個原型可能是另一個型別的實體,所以內部可能也有一個指標指向另一個原型,然后就這樣形成了一條原型鏈,
代碼:
function SuperType() {
this.property = true;
}
SuperType.prototype.getSuperValue = function() {
return this.property;
};
function SubType() {
this.subproperty = false;
}
// 繼承SuperType
SubType.prototype = new SuperType();
SubType.prototype.getSubValue = function () { //注意 不能通過物件字面量的方式添加新方法,否則上一行無效
return this.subproperty;
};
let instance = new SubType();
console.log(instance.getSuperValue()); // true
缺點
1.如果父類實體的屬性是參考型別的時候,其實父類的實體屬性會成為子類的原型屬性,子類創建的所有實體都會共享這些方法,修改一個實體的這個屬性,其他實體的屬性也會被修改
2.子型別在實體化時不能給父型別的建構式傳參
建構式繼承
為了解決原型包含參考值導致的繼承問題,出現了一中’盜用建構式’的技術流行起來,也被稱為’物件偽裝’或’經典繼承’,思路就是在子類構造函
數中呼叫父類建構式,可以使用call() apply()的方法以新創建的物件為背景關系執行函式
function SuperType(name) {
this.colors = ["red","blue","green"];
this.name = name;
}
function SubType(name) {
SuperType.call(this,name);
}
let instance1 = new SuperType('小明')
let instance2 = new SuperType('小白')
instance1.colors .push('yellow')
console.log(instance1) //{name:"小明",colors:["red","blue","green","yellow"]...}
console.log(instance2) //{name:"小白",colors:["red","blue","green"]...}
//可以傳遞引數 也修復了參考的問題 可以繼承多個建構式屬性(call多個)
缺點:
1.只能在建構式中呼叫方法 函式不能重用 就是每次子類生成實體的時候都會生成一次屬性和方法
2. 子類無法訪問到父類原型上的方法
組合繼承
綜合了原型鏈和建構式,將兩者的優點集中了起來,基本的思路是使用原型鏈繼承原型上的屬性和方法,而通過建構式繼承實體屬性,這樣既可以把方法定義在原型上以實作重用,又可以讓每個實體都有自己的屬性,
function SuperType(name){
this.name = name;
this.colors = ["red","blue","green"];
}
SuperType.prototype.sayName = function() {
console.log(this.name);
};
function SubType(name, age){
// 繼承屬性 第二次呼叫
SuperType.call(this, name);
this.age = age;
}
// 繼承方法 第一次呼叫
SubType.prototype = new SuperType();
SubType.prototype.sayAge = function() {
console.log(this.age);
};
let instance1 = new SubType("Nicholas", 29);
instance1.colors.push("black");
console.log(instance1.colors); //["red,blue,green,black"]
instance1.sayName(); // "Nicholas";
instance1.sayAge(); // 29
let instance2 = new SubType("Greg", 27);
console.log(instance2.colors); // ["red,blue,green"]
instance2.sayName(); // "Greg";
instance2.sayAge(); // 27
//可以繼承父類原型上的屬性,可以傳參,可復用, 每個新實體引入的建構式屬性是私有的
缺點
呼叫了兩次父類建構式 比較耗記憶體
原型式繼承
即使不自定義型別也可以通過原型實作物件之間的資訊共享,
function object(person) {
function F() {}
F.prototype = person
return new F()
}
let person = {
name:'小明',
colors:['red','blue']
}
let person1 = object(person)
person1.colors.push('green')
let person2 = object(person)
person1.colors.push('yellow')
console.log(person) //['red','blue','green','yellow']
適用環境: 你有一個物件,想在它的基礎上再創建一個新物件,你需要把這個物件先傳給object() ,然后再對回傳的物件進行適當修改,類似于 Object.create()只傳第一個引數的時候,本質上就是對傳入的物件進行了一次淺復制,缺點就是新實體的屬性都是后面添加的,無法復用
寄生式繼承
與原型式繼承比較接近的一種繼承方式是寄生式繼承,類似于寄生建構式和工廠模式:創建一個實作繼承的函式,以某種方式增強物件,然后回傳這個物件,
function object(person) {
function F() {}
F.prototype = person
return new F()
}
function createAnother(original){
let clone = object(original); // 通過呼叫函式創建一個新物件
clone.sayHi = function() { // 以某種方式增強這個物件
console.log("hi");
};
return clone; // 回傳這個物件
}
寄生式繼承同樣適合主要關注物件,而不在乎型別和建構式的場景,
缺點:通過寄生式繼承給物件添加函式會導致函式難以重用,與建構式模式類似
寄生式組合繼承
最常用的繼承方式,也是最佳的,組合繼承會呼叫兩次父類建構式,存在效率問題,其實本質上子類原型最終是要包含父類物件的所有實體屬性,子類建構式只要在執行時重寫自己的原型就行了,基本思路是不通過呼叫父類建構式給子類原型賦值,而是取得父類原型的一個副本,說到底就是使用寄生式繼承來繼承父類原型,然后將回傳的新物件賦值給子類原型,
//核心代碼
function object(person) {
function F(params) {}
F.prototype = person
return new F()
}
function inheritPrototype(SubType,SuperType) {
let prototype = object(SuperType.prototype) //生成一個父類原型的副本
//重寫這個實體的constructor
prototype.constructor = SubType
//將這個物件副本賦值給 子類的原型
SubType.prototype = prototype
}
function SuperType(name) {
this.name = name;
this.colors = ["red","blue","green"];
}
SuperType.prototype.sayName = function() {
console.log(this.name);
};
function SubType(name, age) {
SuperType.call(this, name);
this.age = age;
}
//呼叫inheritPrototype函式給子類原型賦值,修復了組合繼承的問題
inheritPrototype(SubType, SuperType);
SubType.prototype.sayAge = function() {
console.log(this.age);
};
轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/272905.html
標籤:其他
上一篇:js實作深度優先搜索(DFS)和廣度優先搜索(BFS)
下一篇:前端HTML5十大新特性詳細總結
