給大家詳細講講javaScript裏如何實現繼承?

這階段給大家來講講javaScript裏面的繼承,雖然在ES6中有了繼承,使用extends關鍵字就能實現,而這篇要講的,而是ES6之前的幾種實現繼承的方式;
講繼承,大家肯定需要先了解的是對象,和原型對象以及原型鏈,這個知識點我在上一個帖子中已經有過描述,大家可以去看上篇帖子;那麼首先,大家需要知道,什麼是繼承?

繼承概念

我們可以這樣去理解,子承父業,父親擁有的東西,孩子可以去享用;用於把對象裏面一個公用的屬性和方法提取出來,這樣,孩子就不用去重複去寫這些屬性和方法,直接可以用父親的;

瞭解到了繼承的概念,那麼我們來看看在javaScript裏面如何來實現繼承

繼承實現方式

1、 原型繼承
也就是把系統給我們默認定義的原型對象賦值成我們自己的實例對象,我們來看一段代碼

/**
父親構造函數
/
function Parent() {
this.name = ‘父親’;
}
/
*
孩子構造函數
*/
function Child() {
}
//把原型對象賦值給 父親的實例化對象
Child.prototype = new Parent();
//原型重新賦值了,需要把constructor指向對應的構造,不然匹配不出對應的類型
Child.prototype.constructor =Parent;
var child = new Child();
console.log(child.name);//父親

這裏,把Parent的實例化對象賦值給Child的原型,那麼Child的原型就指向了這個實例化的對象,在我們調用child.name的時候,先會從自身找有沒有這個屬性,如果沒有,找自己的原型對象,而此時的原型對象已經賦值給了Parent的實例,所以通過這點就找到了Parent的name屬性,最後把值’父親’輸出在控制檯,可能這樣描述還是有點暈暈的,沒關係,下面給大家畫一個圖來講解下

在這裏插入圖片描述

上面的圖是我們聲明兩個構造函數以及實例化對象時候的原型關係圖,此時兩者是沒有關係的,而真正讓Child和Parent發生關係的代碼是 Child.prototype = new Parent();這一句,這一句也是把我們Child孩子的原型對方賦值成了Parent的實例對象,當代碼執行完後,他們的關係就發生了變化

在這裏插入圖片描述

這種方式優缺點:

優點:
非常純粹的繼承關係,實例是子類的實例,也是父類的實例
父類新增原型方法/原型屬性,子類都能訪問到
簡單,易於實現

缺點:
不能傳遞參數
來自原型對象的所有屬性被所有實例共享

2、 借用構造函數

借用構造函數,顧名思義,借用別人的構造函數,但是我們一般在聲明構造函數的時候,會用this這個關鍵字來給裏面的屬性賦值,所以在借用構造函數的時候,需要去更改this的指向問題,先來看一段代碼
/**
父親構造函數
/
function Parent(name, age) {
this.name = name;
this.age = age;
}
/
*
孩子構造函數
*/
function Child(name, age) {
//利用call方法,和bind方法差不多意思,但是利用call方法能讓函數直接調用,第一個參數是指向,後面的參數代表傳遞過去的實參
Parent.call(this, name, age);
}
var child = new Child(‘孩子’, 18);
console.log(child.name,child.age);

利用call方法,call方法會直接調用該函數,並且會把該函數裏面的this指向更改,更改成call方法裏面的第一個參數,Parent.call(this, name, age);裏面的this指向的當前實例化Child的對象,那麼在Parent裏面的this也指向的它。所以在輸出的時候能夠取到name屬性和age屬性;

這種方式優缺點:
優點:
解決了方法1中的問題,不會共享原型對象裏面屬性
可以向父類傳遞參數

缺點:
實例並不是父類的實例,只是子類的實例
能繼承父類的實例屬性和方法,不能繼承原型屬性/方法

3、 組合繼承

這種繼承方式是結合了上面兩種,取之精華,來實現既能進行參數傳遞,又可以調用原型裏面定義的方法

/**
父親構造函數
/
function Parent(name, age) {
this.name = name;
this.age = age;
}
Parent.prototype.sayHi = function() {
console.log(this.name + “hi”);
}
/
*
孩子構造函數
*/
function Child(name, age) {
Parent.call(this, name, age);
}
//把原型賦值給Parent的實例化對象
Child.prototype = new Parent();
var child = new Child(‘孩子’, 18);
console.log(child.name, child.age);
child.sayHi();//孩子Hi

來看關係圖:

在這裏插入圖片描述

我們給Parent的原型對象上設置了一個sayHi(),然後把Parent的實例化對象賦值給了Child的原型對象,這樣通過原型鏈的搜索規則,通過new Child();得到Child的實例對象,可以一步一步找到Parent的原型對象,從而拿到sayHi();

特點:
可以繼承實例屬性/方法,也可以繼承原型屬性/方法
既是子類的實例,也是父類的實例
不存在引用屬性共享問題
可傳參
函數可複用

缺陷:
調用了兩次父類構造函數

4、寄生組合繼承

構造函數的目的是爲了複製屬性,Person.call( this, name, age);肯定是不能少的,Child.prototype = new Parent();的目的是爲了獲取到父類原型對象(prototype)上的方法,基於這個目的,有沒有別的方法可以做到,在不需要實例化父類構造函數的情況下,也能得到父類原型對象上的方法呢? 當然可以,我們可以採用寄生式繼承來得到父類原型對象上的方法

function Parent(name, age) { this.name = name;
this.age = age;
}
Parent.prototype.sayHi = function() {
console.log(this.name + " sayHi");
}
function Child(name, age) {
Parent.call(this, name, age);
}
(function() {
var Super = function() {};
//講父類的原型對象賦值給這個超類
Super.prototype = Parent.prototype;
//將實例作爲子類的原型
Child.prototype = new Super();
})();

var child = new Child(‘zs’, 19);
console.log(child.name);
child.sayHi();

其實,說白了寄生組合式繼承就是一個借用構造函數 + 相當於淺拷貝父類的原型對象的方式,下面圖來描述他們的原型關係

在這裏插入圖片描述

原來Super這個構造函數跟Parent構造函數是沒有關係的,但是在我們代碼裏有一個重要的自調用函數

(function() {
var Super = function() {};
//講父類的原型對象賦值給這個超類
Super.prototype = Parent.prototype;
//將實例作爲子類的原型
Child.prototype = new Super()
;})();

這幾行代碼讓兩者產生了關聯,把Parent的原型對象,賦值給了Super,然後把Super的實例對象賦值給了Child的原型對象,如圖:

在這裏插入圖片描述

上圖,通過Super.prototype = Parent.prototype;這行代碼賦值,讓Parent的原型對象跟Super原型對象的指向了同一個內存地址;

以上內容就是對javaScript的繼承的方式進行了一些整理,希望對大家會有幫助!有全套的前端資料免費分享 請加微信:csheima7

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章