今天來點 JavaScript 的原型與繼承!
Prototype(原型)是什麼?
當我們在 JavaScript 中建立物件時,每個物件都有一個隱含屬性 [[Prototype]]
,
叫做 prototype(原型)。原型就像是物件的「模板」,決定了物件的一些基本特性和方法。
藉由以下例子,來看看 prototype
與 __proto__
:
假設我們有一個建構式叫做 Person
,我們用來建立一個人的物件。
這個 Person
建構函式有一個原型且此原型包含了一些方法和屬性。
1 | function Person() { } |
當你在 console 印出 console.log(Person.prototype)
,你會看到:
1
2
3{constructor: ƒ}
constructor: ƒ Person()
[[Prototype]]: Object
以上其實就顯示了 Person.prototype
物件的屬性和原型鏈。
{constructor: ƒ}
:這表示 Person.prototype
物件本身的屬性。
在這裡,你看到一個 constructor
屬性,指向建構子 Person
。
這個屬性告訴 JavaScript,用來建立這個物件的建構子是 Person
。
constructor: ƒ Person()
:這是 constructor
屬性的具體內容。
顯示了 constructor
是一個函數,函數名稱是 Person
,
這表示這個物件是由 Person
建構函數建立的。
[[Prototype]]: Object
:這表示 Person.prototype
物件的原型鏈。
Person.prototype
的原型是 Object
。
這是因為在 JavaScript 中,幾乎所有物件都繼承自 Object
,因此 Object
是原型鏈的頂端。
在這個範例中,Person.prototype
繼承自 Object.prototype
,
而 constructor
屬性告訴我們與之關聯的建構子是 Person
。
接著,我們看一下透過 Person
,建立一個 person1
物件:
1 | function Person() { } |
當我們使用 new Person()
建立一個具體的人的物件時,
這個物件會自動連接到 Person
建構函數的原型。
這意味著這個人的物件可以繼承(就像繼承父母的特徵一樣)原型中的方法和屬性。
這個概念有點像家族。
建構函式就像是家族的創始人,原型就像是家族的傳統,而物件就像是家族的成員,他們可以繼承和分享這些傳統。
透過 person1.__proto__
或 Person.prototype
存取原型時,
你會看到一個對象,其中包含了 constructor
屬性,指向建立這個物件的建構子。
__proto__
是一個非標準的方法,
通常用於訪問物件的原型,可以用来直接訪問和設置物件的原型。
注意!,__proto__
方法並不在 ECMAScript 規範中,
實際上要取得物件的原型會使用Object.getPrototypeOf()
。
Prototype Chain(原型鏈)是什麼?
剛剛在 Person.prototype
有提及到原型鏈的概念,在這我們再一次加深印象!
Prototype Chain(原型鏈)是 JavaScript 中實作繼承和共享屬性/方法的重要機制。
原型鏈是一個由原型物件連接起來的鏈條,決定了在 JavaScript 中尋找屬性和方法時的搜索順序。
當你試圖呼叫一個物件的屬性或方法時,如果該物件本身沒有這個屬性或方法,
JavaScript 會沿著原型鏈向上查找,直到找到對應的屬性或方法,
或者達到原型鏈的末端 Object.prototype
。
Object.prototype
,這是 JavaScript 中的所有物件原型的根。
如果在整個原型鏈上都找不到匹配的屬性或方法,JavaScript 將停止搜尋並傳回 undefined。
因此,透過剛剛建立的 Person
函式,再次觀察物件 person1
與建構函式 Person
之間的原型關係。
1 | function Person() { } |
Prototype Inheritance(原型繼承)是什麼?
原型繼承是 JavaScript 中的一種繼承機制,允許一個物件繼承另一個物件的屬性和方法。
基於原型鏈的概念,子層物件的原型指向父層物件,從而實現屬性和方法的共享和繼承。
讓我們來看看以下例子:
1 | // 定義爸爸層 Person |
關於指向自己的構造函數:
Student.prototype.constructor = Student;
如果建立一個 Student 實例,並嘗試訪問其建構函數,
將返回Person
而不是Student
,這可能會導致混淆和不正確的行為。
為了更正這個問題,需要手動將Student.prototype.constructor
設置為Student
,以確保指向正確的建構函數。這樣,當你建立Student
的實例時,建構函數引用將正確指向Student
。
Brief Summary
- 每個物件都有一個原型,決定了物件的基本屬性和方法。
- 原型鏈由原型物件構成,用於屬性和方法的查找。
- 原型繼承是透過原型鏈來實現的,子層物件可以繼承父層物件的屬性和方法。
References:
➫ MDN
➫ 原型繼承與原型鏈 |ALPHA Camp
➫ JS 原力覺醒 Day22 - 原型共享與原型繼承
➫ 原型基礎物件導向
➫ [教學] JavaScript Prototype (原型) 的用法