你不知道的javascript之繼承

小明與核桃發表於2020-11-18

如果我們學習過物件導向程式語言,比如說Java,那麼我們對繼承這個概念不會陌生。在類似於Java的語言中,繼承基本都是使用extends關鍵字來實現,而在js中卻不是這樣(本篇文章只考慮es6之前繼承的實現方式,es6中已經可以使用extends關鍵字)

什麼是繼承?

繼承的定義:通過【某種方式】讓一個物件可以訪問到另一個物件中的屬性和方法,我們把這種方式稱之為繼承 。比如說:A物件通過繼承B物件,就能直接擁有B物件的屬性和方法,從而可以呼叫B物件的屬性和方法。

js中實現繼承的方法

js中有很多種方法可以實現繼承,這裡我們只討論三種方法:原型鏈繼承、構造繼承、組合繼承。

我們先定義一個父類建構函式:

function Person(name, age) {
	//定義父類的屬性和方法
	this.name = name;
	this.age = age;
	
	this.greed = function() {
		console.log('Hello, I am ' + this.name);
	}
}
//定義一個父類原型上的方法
Person.prototype.getInfo = function() {
	console.log(this.name + ',' + this.age);
}

原型鏈繼承

原型鏈繼承就是修改原型鏈實現繼承,通過將子類的prototype指向父類的例項來實現。

//定義子類的建構函式
function Student1() {
	
}
Student1.prototype = new Person();//子類的prototype指向父類的例項

//修改從父類繼承過來的屬性
Student1.prototype.name = 'xiaoming';
Student1.prototype.age = 22;
let student1 = new Student1();
//呼叫父類方法
student1.getInfo();//xiaoming,22

在原型鏈繼承這種方式中,student1物件既是子類的例項,又是父類的例項。缺點是在子類的建構函式中無法通過傳參的方式對父類繼承的屬性值進行修改,只能通過修改prototype的方式進行修改。

構造繼承

構造繼承就是在子類的建構函式中呼叫父類的建構函式實現繼承

//定義子類的建構函式
function Student2(name, age, sex) {
	Person.call(this);//呼叫父類的建構函式
	//修改父類的屬性
	this.name = name;
	this.age = age;
	//子類自身的屬性
	this.sex = sex;
}
let student2 = new Student2('xiaoming', 22, 'male');
student2.greed();//Hello,I am xiaoming
student2.getInfo()//報錯:student2.getInfo is not a function

構造繼承避免了原型鏈繼承的缺點,直接在子類中呼叫父類的建構函式,這樣,student2只是子類的例項。然而這種方式的缺點是子類例項只能呼叫父類例項中定義的方法,不能呼叫父類原型上定義的方法。

組合繼承

組合繼承是原型鏈繼承和構造繼承的組合體

//定義子類的建構函式
function Student3(name, age, sex) {
	Person.call(this);//(第一次)呼叫父類的建構函式
	//修改父類的屬性
	this.name = name;
	this.age = age;
	//子類自身的屬性
	this.sex = sex;
}
Student3.prototype = new Person();//(第二次)呼叫父類的建構函式

let student3 = new Student3('xiaoming',22,'male');
student3.greed();//Hello,I am xiaoming
student3.getInfo();//xiaoming,22

組合繼承結合了原型鏈繼承和構造繼承方式的優點,唯一的問題是需要呼叫父類的建構函式兩次,這造成了一定的記憶體浪費。

相關文章