Photo by Aida L on Unsplash |
前言
過去在學習 JavaScript 的時候,常常會聽到原型鏈這個名詞,那時候看了許多的資料,卻還是被它錯綜複雜的關係給迷惑。比如: __proto__ 和 prototype 的區別是什麼? 它們之間又有什麼關係。直到最近在 Udemy 上了 克服 JS 的奇怪部分 這門課之後,才對原型鏈有了較深刻的體會。下面就把我學習的心得寫出來和大家交流交流~~
原型鏈到底是什麼?
原型鏈其實是 JavaScript 實現繼承的一個機制。這是什麼意思呢? Anthony Alicea 用了一個簡單的例子來說明。
在上面的圖中,obj 代表一個物件,而這個物件有一個 prop1 的屬性。我們可以使用 obj.prop1 來存取它。
不過,除了 prop1 之外, JavaScript 引擎還偷偷替 obj 增加了一個屬性: __proto__。這個 __proto__ 其實指向了第二個物件。我們可以看到,這個物件有一個 prop2 的屬性。此時,當我們使用了obj.prop2,會發生什麼事呢?
首先,JavaScript 引擎會先搜尋 obj 物件本身有沒有 prop2。因為 obj 沒有 prop2,所以JavaScript 引擎會繼續搜尋它的 __proto__ 所指向的第二個物件。這時候,因為第二個物件有一個 prop2,因此它就會回傳這個 prop2。
同理,第二個物件也會有它的 __proto__,而這個 __proto__ 又會指向第三個物件。因此,當我們使用 obj.prop3 時,就會取得第三個物件的 prop3。看到這裡,我們可以漸漸體會到,JavaScript 是使用 __proto__ 這個屬性來將所有具有繼承關係的物件串接在一起。這看起來就像一條鍊子一樣,所以我們才會將其稱做原型鏈。
上點程式碼吧!
我們用一段程式碼來具體說明一下上面的例子。
var person = { firstname: 'Default', lastname: 'Default', getFullName: function() { return this.firstname + ' ' + this.lastname; } } var john = { firstname: 'John', lastname: 'Doe' }
// 僅限於示範,實際上不要這麼做
john.__proto__ = person;
console.log(john.getFullName());
這段程式碼中有兩個物件: person 與 john。這兩個物件都有 firstname 與 lastname 兩個屬性,而 person 還有一個 getFullName() 的函式。剛剛有提到,每一個物件都會有一個 __proto__ 的屬性,而這裡我們將 john 的 __proto__ 指定為 person (這裡只是用來示範,實際上由於效能的考量,並不會這樣寫)。
如此一來,當執行 john.getFullName() 時,JavaScript 引擎首先會去找 john 本身有沒有 getFullName()。因為找不到,所以會繼續去搜尋 person ,最後就會找到了 person 的 getFullName()。因此,上面程式碼執行的結果會顯示:
> John Doe
由這個例子可以看出,john 執行的 getFullName() 是 person 的版本,這就好像 john 繼承了 person 一樣。因此,一開頭我們才會說原型鏈是 JavaScript 實現繼承的一個機制。
小結
在本篇文章中,我們對 JavaScript 的原型鏈有了初步的認識。不過有關 JavaScript 物件導向的議題還包括 function constructor、new operator 等等。這部分就留待下次討論吧~~
沒有留言:
張貼留言