原生Javascript實現繼承方式及其優缺點詳解

前言

最近在復習javascript的一些基礎知識,為開啟新的征程做準備。所以開始記錄一些自己學習的內容。

那今天的主題是 js的原生繼承方式

廢話少說,上代碼!

首先是我們的父類代碼。

在這裡我們創建一個Person的類作為父類,它的構造函數需要2個參數name和age。

然後我們在它的原型上添加一個sayHi的方法。

//父類
function Person (name, age) {
    this.name = name || 'no name';
    this.age = age || 0;
}

Person.prototype.sayHi = function () {
    console.log('Hi, I\'m ' + this.name + ' and i\'m ' + this.age + ' years old!');
}

var p = new Person('A',20);
p.sayHi();//Hi, I'm A and i'm 20 years old!

原型繼承

//原型繼承
function Teacher(){
}

Teacher.prototype=new Person('B',22);
Teacher.prototype.constructor=Teacher;

var t = new Teacher();
t.sayHi();//Hi, I'm B and i'm 22 years old!
console.log(t instanceof Person);//true
console.log(t instanceof Teacher);//true

優點

從上面的代碼來看,Teacher 的實例擁有瞭 Person 的屬性和方法。並且實例對象既是 Person的實例也是 Teacher的實例。而且這種繼承方式特別的簡單。

缺點

我們可以很容易的就發現Teacher類的 name和 age是固定的,都是name=B和age=22,換句話說就是我們無法實現按照我們的意願給父類的構造函數傳參。並且一個我們不能給一個 Teacher 指定多個原型,也就是沒法 多繼承。然後我們看下下面這段代碼:

var t1 = new Teacher();
var t2 = new Teacher();
Teacher.prototype.name = "C";
t1.sayHi();//Hi, I'm C and i'm 22 years old!
t2.sayHi();//Hi, I'm C and i'm 22 years old!

上面這段代碼中我們可以看到當原型中的屬性或者方法被改變時,所有的子類實例的屬性和方法也會跟著被改變,也就是原型繼承的另一個缺點:所有子類共享同一個原型對象

這裡說到瞭原型,我很早之前也寫過一個關於原型的隨筆,不過可能也是有些模糊,現在的理解和當時有所不同,我會在後面重新寫一篇關於原型的隨筆。(寫好瞭我會附上連接)

構造函數繼承

//構造函數繼承
function Teacher (name, age) {
    Person.call(this, name, age);
}

var t1 = new Teacher('B', 22);
var t2 = new Teacher('C', 30);
console.log(t1.name);//B
console.log(t2.name);//C
console.log(t1 instanceof Person);//false
console.log(t1 instanceof Teacher);//true
t1.sayHi();//TypeError: t1.sayHi is not a function
t2.sayHi();//TypeError: t1.sayHi is not a function

優點

相對於 原型繼承 , 構造函數繼承解決瞭所有的子類實例共享統一原型的問題,也可以給父類的構造函數傳參,並且我們可以在子類的構造函數中調用多個父類的構造函數,實現所謂的多繼承(這裡的多繼承是指子類通過call,apply等方法去調用父類的構造函數使其擁有父類的屬性和方法,但是js中一個函數對象隻存在一個 prototype,所以其實我們沒法通過原型鏈的形式去體現出多繼承)

缺點

上面的代碼中我們可以看出創建的實例隻是 子類的實例 並不是 父類的實例 ,不能直觀的體現出繼承,這種繼承方式也無法繼承父類的原型上的屬性和方法。

組合式繼承

//組合式繼承
function Teacher (name, age) {
    Person.call(this, name, age);
}

Teacher.prototype = new Person();
Teacher.prototype.constructor = Teacher;


var t1 = new Teacher('B', 22);
var t2 = new Teacher('C', 30);
Teacher.prototype.name = "D";
console.log(t1.name);//B
console.log(t2.name);//C
t1.sayHi();//Hi, I'm B and i'm 22 years old!
t2.sayHi();//Hi, I'm C and i'm 30 years old!
console.log(t1 instanceof Person);//true
console.log(t1 instanceof Teacher);//true

組合式繼承就是結合瞭原型繼承和構造函數繼承的優點,解決瞭兩種方式存在的一些缺點。但是我們會發現每當我們去創建一個子類實例的時候都會去創建一個父類的實例,盡管父類實例不是同一個實例(內存地址不一樣),但是他們其實屬性和方法上完全一致,所以我們通過下面這種(寄生式組合繼承)方式完善它,以避免不必要的實例構造。

寄生式組合繼承

//寄生式組合繼承
function Teacher (name, age) {
    Person.call(this, name, age);
}

Teacher.prototype = Object.create(Person.prototype);
Teacher.prototype.constructor = Teacher;


var t1 = new Teacher('B', 22);
var t2 = new Teacher('C', 30);
Teacher.prototype.name = "D";
console.log(t1.name);//B
console.log(t2.name);//C
t1.sayHi();//Hi, I'm B and i'm 22 years old!  
t2.sayHi();//Hi, I'm C and i'm 30 years old!
console.log(t1 instanceof Person);//true
console.log(t1 instanceof Teacher);//true 

上面的方式解決瞭我們沒創建一個子類實例都去創建一個父類實例的問題,這也是最為常用的一種js的繼承方式,如果我們通過Babel去把ES6中的class的繼承轉成ES5的代碼,我們會發現就是用的寄生式組合繼承。

總結

到此這篇關於原生Javascript實現繼承方式及其優缺點的文章就介紹到這瞭,更多相關原生Javascript繼承方式內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!

推薦閱讀: