JavaScript 中的繼承

原文
不像其它語言,js 沒有類的概念。它使用原型和原型鏈來實現繼承。如果你不知道什麼是原型,請先看這篇文章

原型鏈

原型鏈指的是一個對象的 __proto__ 指向另一個對象,而不是指向構造函數。如果這另一個對象的 proto 又繼續指向另一個對象,這就會產生一個鏈式關係,這就是原型鏈。

這裏寫圖片描述

下面實現一個原型鏈


//SuperType constructor function
function SuperType(){
    this.name = "Virat"
}

//SuperType prototype
SuperType.prototype.getSuperName = function(){
    return this.name
}

//SubType prototype function
function SubType(){
    this.age = 26
}

//Inherit the properties from SuperType
SubType.prototype = new SuperType();

//Add new property to SubType prototype
SubType.prototype.getSubAge = function(){
    return this.age;
}

//Create a SubType object
var subTypeObj = new SubType();
console.log(subTypeObj.name); //Output: Virat
console.log(subTypeObj.age); //Output: 26
console.log(subTypeObj.getSuperName()); //Output: Virat
console.log(subTypeObj.getSubAge()); //Output: 26

這裏寫圖片描述

上面的代碼定義了兩個構造函數,SuperTypeSubType 。默認情況下,SubType.prototype 有一個指向構造函數本身的屬性 construtor ,一個繼承 object 的屬性 __proto__

//繼承 SuperType 的屬性
SubType.prototype = new SuperType();

這行代碼覆蓋了 SubType 構造函數的默認 prototype 屬性,並使得 SubType.prototype 指向了 SuperType 的一個對象。

這意味着, SubType.prototype 擁有了 SuperType 對象的所有屬性和方法。

//往 SubType 原型中添加屬性
SubType.prototype.getSubAge = function(){
    return this.age;
}

在 SubType 的默認 prototype 被覆蓋之後,通過上面這段代碼,我們在從 SuperType 繼承的基礎上往原型對象中增加了一個方法 getSubAge()

注意: 在繼承之後,新的方法必須被增加到 SubType 中,因爲繼承覆蓋了 SubType 原有的原型對象。

控制檯輸出

這裏寫圖片描述

這裏寫圖片描述

這裏寫圖片描述

注意: getSuperName() 方法保留在 SuperType.prototype 對象中,但 name 屬性在 SubType.prototype 中。這是因爲 getSuperName() 是一個原型對象的方法。SubType.prototype 現在是一個 SuperType 的實例,所以屬性被存儲在這裏。也要注意 SubType.prototype.constructor 指向了 SuperType,因爲SubType.prototype 中的 constructor 屬性被覆蓋了。

原型鏈的問題

因爲所有父類原型的屬性都被子類共享,所以如果一個子類對象修改了父類原型的屬性,其它的子類對象也會受影響。這個問題經常被抱怨。

要解決這個問題,我們在構造函數中繼承實例屬性,用原型鏈繼承公共的屬性和方法。


//SuperType constructor function
function SuperType(firstName, lastName){
    this.firstName = "Virat",
    this.lastName = "Kohli",
    this.friends = ["Ashwin", "Jadeja"]
}

//SuperType prototype
SuperType.prototype.getSuperName = function(){
    return this.firstName + " " + this.lastName;
}

//SubType prototype function
function SubType(firstName, lastName, age){
    //Inherit instance properties
    SuperType.call(this, firstname, lastName);
    this.age = age;
}

//Inherit methods and shared properties
SubType.prototype = new SuperType();

//Add new property to SubType prototype
SubType.prototype.getSubAge = function(){
    return this.age;
}

//Create SubType objects
var subTypeObj1= new SubType("Virat", "Kohli", 26);
var subTypeObj2 = new SubType("Sachin", "Tendulkar", 39);

//Modify the friends property using the subTypeObj1
subTypeObj1.friends.push("Amit");

console.log(subTypeObj1.friends);//["Ahswin", "Jadega", "Amit"]
console.log(subTypeObj2.friends);//["Ashwin", "Jadega"]

//subTypeObj1
console.log(subTypeObj1.firstName); //Output: Virat
console.log(subTypeObj1.age); //Output: 26
console.log(subTypeObj1.getSuperName()); //Output: Virat Kohli
console.log(subTypeObj1.getSubAge()); //Output: 26

//subTypeObj2
console.log(subTypeObj2.firstName); //Output: Sachin
console.log(subTypeObj2.age); //Output: 39
console.log(subTypeObj2.getSuperName()); //Output: Sachin Tendulkar
console.log(subTypeObj2.getSubAge()); //Output: 39

現在來分析一下上面的代碼。首先,我們定義了 SuperType 構造函數,它有三個屬性 firstName, lastNamefriends,這三個屬性是實例屬性,然後我們在 SuperType 的原型上定義了 getSuperName 方法。

現在,讓我們來看一下如何定義 SubType 構造函數

//SubType prototype function
function SubType(firstName, lastName, age){
    //Inherit instance properties
    SuperType.call(this, firstname, lastName);
    this.age = age;
}

這裏我們定義了 SubType 構造函數,在構造函數裏面,我們使用 call 調用了 SuperType 構造函數。通過執行 call 的調用,我們在這個被創建的 SubType 實例對象的上下文中執行了 SuperType 的構造函數。在繼承 SuperType 的實例屬性之後,我們增加了一個 age 屬性到 SubType 的構造函數中。

//繼承公共方法和屬性
SubType.prototype = new SuperType();

到此爲止我們僅僅繼承了 SuperType 的實例屬性,但是公共的屬性和方法沒有繼承,我們通過上面這行代碼繼承它們。

一旦執行了上面的代碼,我們繼承了 SuperType 構造函數的所有屬性。

//增加新屬性到原型中
SubType.prototype.getSubAge = function(){
    return this.age;
}

通過上面這行代碼我們增加了在從 SuperType 中繼承的屬性中又增加了一個新的屬性。

下面我們通過創建一個 SubType 實例來理解這個過程。

var subTypeObj1= new SubType("Virat", "Kohli", 26);

執行上面這行代碼之後,三個參數 (”Virat”, “Kohli”, 26)被傳遞到 SubType 構造函數中。SubType 構造函數通過 SuperType.call(this, firstname, lastName) 調用了 SuperType 構造函數,SuperType 構造函數在 subTypeObj1 的上下文中執行,隨之增加 firstName, lastName, friends 屬性到 subTypeObj1 對象中。在 SuperType.call(this, firstname, lastName) 之後,SubType 構造函數增加了 age 屬性到 subTypeObj1 對象中。

因此目前 subTypeObj1 對象擁有 firstName,lastName ,friends 和 age 屬性。此時 SubType 構造函數有如下公共的屬性和方法:

  1. getSuperName
  2. getSubAge

subTypeObj1 從 SubType 構造函數中繼承上面所有屬性。

這裏寫圖片描述

發佈了50 篇原創文章 · 獲贊 26 · 訪問量 16萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章