Original Article: JavaScript — Inheritance, delegation patterns and Object linking
Author: NC Patro
Learn about inheritance, delegation patterns, and objects linked to other objects in JavaScript (prototype inheritance).
什麼是繼承#
在大多數基於類別的物件導向語言中,繼承是一種機制,讓一個物件可以獲得另一個物件的所有屬性和方法。儘管在 ES2015
中提出了 class
關鍵字,但 JavaScript
並不是一門基於類別的語言,它只是一種語法糖,本質上仍然是基於原型鏈的方式。
經典繼承與原型繼承#
經典繼承(非 JavaScript)#
Vehicle
是父類別,v1
和v2
是Vehicle
的實例。Car
是Vehicle
的子類別,而c1
和c2
是Car
的實例。- 當我們繼承類別時,經典繼承會將父類別的行為複製到子類別中,然後父子類別就成為獨立的實體。
- 這就像是汽車是用工具和汽車圖紙製造出來的,但製造完成後它們是獨立的個體,因為它們只是複製,所以它們之間沒有關聯,這就是所有箭頭向下(屬性和行為向下傳遞)的原因。
原型繼承(行為委派模式)#
v1
和v2
關聯到Vehicle.prototype
,因為它們是通過 new 創建的。- 同樣地,
c1
和c2
關聯到Car.prototype
,而Car.prototype
關聯到Vehicle.prototype
。 - 在
JavaScript
中,當我們創建一個物件時,它不是複製屬性或行為,而是創建一個連結。在繼承類別時也會創建類似的連結。 - 與經典的非
JavaScript
繼承相比,所有連結指向相反的方向,因為它是行為委派連結。這些連結稱為原型鏈。 - 這種模式被稱為行為委派模式,通常稱為
JavaScript
中的原型繼承。
您可以通過閱讀這篇文章 JavaScript - 原型 來深入了解原型鏈。
原型繼承的例子#
- 使用
Object.create()
實現經典繼承。 - 在下面的程式碼片段中,
Car.prototype
和Vehicle.prototype
通過Object.create()
函數相連。
// Vehicle - 超類別
function Vehicle (name) {
this.name = name;
}
// 超類別的方法
Vehicle.prototype.start = function () {
return "engine of " + this.name + " starting...";
}
// Car - 子類別
function Car (name) {
Vehicle.call(this, name); // 調用超類別的建構函數
}
// 子類別擴展超類別
Car.prototype = Object.create(Vehicle.prototype);
// 子類別的方法
Car.prototype.run = function () {
console.log("Hello " + this.start());
}
// 子類別的實例
var c1 = new Car("Fiesta");
var c2 = new Car("Baleno");
// 存取內部存取超類別方法的子類別方法
c1.run(); // "Hello engine of Fiesta starting..."
c2.run(); // "Hello engine of Baleno starting..."
- 在上述程式碼中,由於下面的原型鏈,物件
c1
可以存取run()
方法和start()
方法。如下圖所示,我們可以看到c1
沒有這樣的方法,但它有向上的連結。 - 上面程式碼中的
this
只是每個方法當前的執行上下文,即c1
和c2
。
您可以瀏覽這篇文章 JavaScript - 關於 this 和 new 的所有內容 來詳細了解this關鍵字。
上述程式碼的圖解表示:
與其他物件關聯的物件#
- 現在我們將簡化先前的繼承示例程式碼,只關注物件之間的連結。
- 因此,我們將嘗試移除 .prototype,constructor 和 new 關鍵字,只考慮物件。
- 我們將使用
Object.create()
函數來創建函數之間的所有連結。
以下是先前示例程式碼的簡化版:
// 包含初始化方法的基礎物件
var Vehicle = {
init: function (name) {
this.name = name;
},
start: function () {
return "engine of " + this.name + " starting...";
}
}
// 在子物件和基礎物件之間創建的委派連結
var Car = Object.create(Vehicle);
// 子物件的方法
Car.run = function () {
console.log("Hello " + this.start());
};
// 具有委派連結的實例物件指向子物件
var c1 = Object.create(Car);
c1.init('Fiesta');
var c2 = Object.create(Car);
c2.init('Baleno');
c1.run(); // "Hello engine of Fiesta starting..."
c2.run(); // "Hello engine of Baleno starting..."
上述程式碼的圖解展示:
- 現在我們可以看到,我們如何消除了 new,所有 .prototype,建構函數和呼叫方法的複雜性,並且仍然實現了相同的結果。
- 唯一重要的是
c1
連結到一個物件,然後再連結到另一個物件,依此類推。 - 這也被稱為物件委派模式。
總結#
在程式碼中使用原型繼承和原型鏈之前,了解它們是很重要的,以避免複雜性。
參考資料:You Don't Know JS 系列書籍