持續更新中.....
繼承一共有六種方式:
分別是:
1. 原型鏈繼承
2. 借用構造函數繼承
3. 組合繼承
4. 原型式繼承
5. 寄生式繼承
6. 寄生組合式繼承
1. 原型鏈繼承
原型鏈繼承是實現繼承的主要方法。
- 基本思想: 利用原型讓一個引用類型繼承另外一個引用類型的屬性和方法。
瞭解一下構造函數,原型和實例的關係?
- 每個構造函數都有一個原型對象,原型對象都包含指向構造函數的指針(
prototype
),而實例包含了指向原型的指針(__proto__
)
實例的__proto__
屬性等於構造函數的prototype
屬性
實例 -> 原型 -> 構造函數
原型鏈的基本概念?
- 如果試圖引用對象(實例
instance
)的某個屬性,首先會從對象內部開始尋找該屬性,直至找不到,然後纔會到對象的原型(instance.prototype
)去尋找該屬性。 - 如果原型對象指向另一個類型的實例,即
constructor1.prototype = instance2
如果試圖引用constructor1
構造的實例instance1
的某個屬性p1
:
- 首先會在
instance1
內部找一遍 - 接着在
instance.__proto__
(即constructor1.prototype
)中找一遍,實際上constructor1.prototype
就是instance2
,也就是說在instance2
找到了p1
屬性。 - 如果
instance2
還是沒有p1
屬性,就會持續在instance2.__proto__
(即constructor2.prototype
)中尋找,一直這樣下去,直到找到爲止。或者找到Object
的原型對象上(null
) - 這樣就形成路徑了:
instance1-->instance2-->....-->Object.prototype
- 類似鏈狀的結構稱爲“原型鏈”
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(){
return this.subProperty;
}
// 重寫父類型中的方法
SubType.prototype.getSuperValue(){
return false;
}
let instance = new SubType();
instance.getSuperValue(); // false
- 上述代碼中。第一個方法
getSubValue()
被添加到了SubType
中。而getSuperValue()
是原型鏈中已經存在的一個方法,但是重寫這個方法會屏蔽原來的方法。 - 也就是說當通過
SuperType
的實例調用getSuperValue()
時,還會繼續調用原來的那個方法
⚠️通過原型鏈繼承時,不能使用對象字面量創建原型方法。這樣會重寫原型鏈。
如:
function SuperType(){
this.property = true;
}
SuperType.prototype.getSuperValue(){
return this.property;
}
function SubType (){
this.subProperty = false;
}
// 繼承SuperType
SubType.prototype = new SuperType();
// 使用字面量添加新方法會導致上一條代碼無效(即取代)
SubType.prototype = {
getSubValue: function(){
return this.subProprty;
},
someMethod: function(){
return false;
}
}
let instance = new SubType();
instance.getSuperValue(); // 報錯
2. 借用構造函數繼承
構造函數繼承有時候又叫做“僞造對象或經典繼承”
- 基本思想: 在子類型構造函數的內部調用父類型構造函數。
- 但是,一般函數只不過是在特定環境中執行代碼的對象,因此可以通過使用
apply
,call
方法可以在新創建的對象上執行構造函數。
示例:
function SuperType(){
this.arr = [1,2,3];
}
// 借用構造函數繼承
function SubType(){
// 繼承SuperType
SuperType.call(this);
}
let instance1 = new SubType();
instance1.arr.push(4);
instance1.arr; // [1,2,3,4]
let instance2 = new SubType();
instance2.arr; // [1,2,3]
- 在上述代碼中,在子類型的內部調用了父類型的構造函數
- 使用了
call
方法,實際上是在新創建的SubType
實例環境下調用了SuperType
構造函數 - 這樣,就會在新的
SubType
對象上執行SuperType()
函數定義的所有對象初始化代碼。結果SubType
的每個實例都會具有SuperType
對象中的屬性或方法的副本
這讓我想到了,ES6中類的繼承爲什麼要在子類的構造函數中執行super()
方法,就是這個道理。
構造函數繼承相對原型鏈繼承的一個優勢?
- 可以在子類型的構造函數中向父類型的構造函數傳遞參數。
示例:
function SuperType(name){
this.name = name;
}
function SubType(){
// 繼承 SuperType,同時傳遞參數
Supertype.call(this, 'Jack');
// 實例屬性
this.age = 24;
}
let instance = new SubType();
instance.name; // Jack
instance.age; // 24
3. 組合繼承
組合繼承有時候又叫“僞經典繼承”,是將原型鏈繼承和借用構造函數繼承組合到一起的繼承。
- 基本思想: 使用原型鏈實現對原型屬性的方法的繼承,通過即用構造函數來實現對實例屬性的繼承。這樣即通過在原型上定義方法實現了函數複用,又能夠保證每個實例都有它自己的屬性。
示例:
function SuperType(name){
this.name = name;
this.arr = [1,2,3];
}
SuperType.prototype.sayName = fuction(){
alert(this.name);
}
function SubType(name , age){
// 繼承SuperType
SuperType.call(this, name);
this.age = age;
}
// 繼承方法
SubType.prototype = new SuperType();
SubType.prototype.constructor = SubType;
SubType.prototype.sayAge(){
alert(this.age);
}
let instance1 = new SubType('Jack', 24);
instance1.arr.push(4);
instance1.sayName(); // Jack
instance1.sayAge(); // 24
instance!.arr; // [1,2,3,4]
let instance2 = new SubType("Mary", 23);
instance2.arr; // [1,2,3]
instance2.sayName(); // Mary
instance2.sayAge(); // 23
- 上述代碼中,
SuperType
構造函數指定兩個屬性:name
,arr
。SuperType
的原型定義了sayName()
方法。 SubType
構造函數在調用SuperType
構造函數時傳入了name
參數,緊接着定義了sayAge()
方法- 這樣就可以讓兩個不同的
SubType
實例分別擁有自己的屬醒(arr
,name
,age
) - 也可以使用相同的方法(
sayName()
,sayAge()
)
4.原型式繼承
原型式繼承要求必須有一個對象作爲另一個對象的基礎。
- 基本思想: 藉助原型基於已有的對象創建新的對象。
給出函數:
function object(o){
}
通過Object.create()
方法實現原型式繼承。
- 這方法接收兩個參數
- 一個用作新對象的原型對象(可選)
- 另外一個是一個對象,爲新對象添加額外的屬性。
示例:
let person = {
name: 'Jack',
age: 24,
friends: [1,2,3]
}
// 繼承person
let otherPerson = Object.create(person,{
name: {
value: "Mary"
}
})
alert(otherPerson.name); // Mary
5.寄生式繼承
- 基本思想:創造一個僅用於封裝繼承過程的函數,該函數在內部以某種方式來增強對象,最後返回對象。
示例:
function createOther(obj){
let clone =
}