關於JS的六種繼承

持續更新中.....

繼承一共有六種方式:
分別是:
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. 借用構造函數繼承

構造函數繼承有時候又叫做“僞造對象或經典繼承”

  • 基本思想: 在子類型構造函數的內部調用父類型構造函數。
  • 但是,一般函數只不過是在特定環境中執行代碼的對象,因此可以通過使用applycall方法可以在新創建的對象上執行構造函數。
    示例:
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構造函數指定兩個屬性:namearrSuperType的原型定義了sayName()方法。
  • SubType構造函數在調用SuperType構造函數時傳入了name參數,緊接着定義了sayAge()方法
  • 這樣就可以讓兩個不同的SubType實例分別擁有自己的屬醒(arr,name,age
  • 也可以使用相同的方法(sayName(), sayAge())

4.原型式繼承

原型式繼承要求必須有一個對象作爲另一個對象的基礎。

  • 基本思想: 藉助原型基於已有的對象創建新的對象。

給出函數:

function object(o){

}

通過Object.create()方法實現原型式繼承。

  • 這方法接收兩個參數
  1. 一個用作新對象的原型對象(可選)
  2. 另外一個是一個對象,爲新對象添加額外的屬性。

示例:

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 = 
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章