物件導向:類的定義和繼承的幾種方式

JOKEthel發表於2020-12-21

類的定義、例項化

類的定義/類的宣告

方式一: 用建構函式模擬類(傳統寫法)

function Animal1() {
	this.name = 'smyhvae'; //通過this,表明這是一個建構函式
}

方式二: 用 class 宣告(ES6的寫法)

class Animal2 {
	constructor() {  //可以在建構函式裡寫屬性
		this.name = name;
	}
}

控制檯的效果:
控制檯
例項化

類的例項化很簡單,直接 new 出來即可。

console.log(new Animal1(),new Animal2()); //例項化。如果括號裡沒有引數,則括號可以省略

控制檯

繼承的幾種方式

繼承的本質就是原型鏈。

方式一: 藉助建構函式

function Parent1() {
	this.name = 'parent1 的屬性';
}

function Child1() {
	Parent1.call(this);         //【重要】此處用 call 或 apply 都行:改變 this 的指向
	this.type = 'child1 的屬性';
}

console.log(new Child1);

【重要】上方程式碼中,最重要的那行程式碼:在子類的建構函式裡寫了Parent1.call(this);,意思是:Parent的建構函式在child的建構函式中執行。發生的變化是:改變this的指向parent的例項 --> 改為指向child的例項。導致 parent的例項的屬性掛在到了child的例項上,這就實現了繼承。

列印結果:
控制檯

上方結果表明child先有了 parent 例項的屬性(繼承得以實現),再有了child 例項的屬性。

分析:
這種方式,雖然改變了 this 的指向,但是,Child1 無法繼承 Parent1 的原型。也就是說,如果我給 Parent1 的原型增加一個方法:

Parent1.prototype.say = function () {
};

上面這個方法是無法被 Child1 繼承的。如下:
例子
方法二: 通過原型鏈實現繼承

/*
通過原型鏈實現繼承
*/
function Parent() {
	this.name = 'Parent 的屬性';
}

function Child() {
	this.type = 'Child 的屬性';
}

Child.prototype = new Parent(); //【重要】

console.log(new Child());

列印結果:
控制檯
【重要】上方程式碼中,最重要的那行:每個函式都有prototype屬性,於是,建構函式也有這個屬性,這個屬性是一個物件。現在,我們把Parent的例項賦值給了Child的prototye,從而實現繼承。此時,Child建構函式、Parent的例項、Child的例項構成一個三角關係。於是:

  • new Child.__proto__ === new Parent()的結果為true

分析:

這種繼承方式,Child 可以繼承 Parent 的原型,但有個缺點:

缺點是:如果修改 child1例項的name屬性,child2例項中的name屬性也會跟著改變。

如下:
例子
上面的程式碼中, child1修改了arr屬性,卻發現,child2arr屬性也跟著改變了。這顯然不太好,在業務中,兩個子模組應該隔離才對。如果改了一個物件,另一個物件卻發生了改變,就不太好。

造成這種缺點的原因是:child1和child2共用原型。即:chi1d1.__proto__ === child2__proto__是嚴格相同。而 arr方法是在 Parent 的例項上(即 Child例項的原型)的。

方式三: 組合的方式:建構函式 + 原型鏈

就是把上面的兩種方式組合起來:

 /*
組合方式實現繼承:建構函式、原型鏈
*/
function Parent3() {
	this.name = 'Parent 的屬性';
	this.arr = [1, 2, 3];
}

function Child3() {
	Parent3.call(this); //【重要1】執行 parent方法
	this.type = 'Child 的屬性';
}
Child3.prototype = new Parent3(); //【重要2】第二次執行parent方法

var child = new Child3();

這種方式,能解決之前兩種方式的問題:既可以繼承父類原型的內容,也不會造成原型裡屬性的修改。

這種方式的缺點是:讓父親Parent的構造方法執行了兩次。

ES6中的繼承方式,一帶而過即可,重點是要掌握ES5中的繼承。

相關文章