這篇筆記主要說明了 JavaScript 中非常重要的概念,也就是繼承(inheritance)
、原型(prototype)
和原型鍊(prototype chain)
。
繼承 ( inheritance )
1. 定義
繼承即一個物件擁有另一個物件的屬性(property)
和方法(method)
2. 古典繼承及原型繼承
- 古典繼承(classical inheritance)
C#
、JAVA
常用到的物件繼承方式,古典繼承很流行,也解決了很多問題,但樹狀結構物件的互動模式,一但數量增加,很容易產生複雜、龐大的集合。
而對於古典繼承,講師提到一種很貼切的形容,就像是今天你打開了燈,馬桶卻突然沖水了。 - 原型繼承(prototypal inheritance)
JavaScript
的物件繼承種類,相對古典繼承簡易、彈性。
原型鍊 ( prototype chain )
由於 JavaScript使用的是 原型繼承 prototypal inheritance
,所以必然會包含原型(prototype)的概念。
一個物件裡面除了所給予的屬性值外,另外也包含一個原型物件
__proto__
。
1 | obj.prop1 |
現在有一個物件obj
,而這個物件包含屬性(property)稱做prop1
,並可以使用obj.prop1
來取用這個屬性。
1 | obj.prop2 |
如果要存取obj.prop2
,JavaScript 引擎會先在 obj
這個物件的屬性裡去尋找有沒有叫作 prop2
的屬性,如果它找不到,這時候它就會再進一步往該物件的原型物件 __proto__
裡面去尋找。所以,雖然我們輸入 obj.prop2
的時候會得到回傳值不報錯,但實際上這不是 obj
裡面直接的屬性名稱,而是在 obj
的 __proto__
裡面找到的屬性名稱( 即obj.proto.prop2
但我們不需要這樣打)。
1 | obj.prop3 |
同樣的,每個物件都包含一個原型物件__proto__
,包含__proto__
本身也不例外,所以如果輸入obj.prop3
,JavaScript會先在obj
裡尋找,找不到會再往obj.__proto__
找,若還是找不到,他就會再往obj.__proto__.__proto__
繼續找下去,找到最底層為止。
物件往原型物件查找,再往原型物件的原型物件查找,再往原型物件的原(下略)一直找,鏈接下來的這個型態,就稱為
Prototypal Chain原型鏈
。
原型鏈
查找與範圍鏈scope chain
(外部參照)是不一樣的東西,前者是去物件的原型對象找屬性和方法,後者是往外部執行環境找變數,這兩者不可搞混。
JS的原型繼承特性,不同的物件可以指向同一個原型物件
若今天有個物件obj2
,它可以是和obj1
有同樣的原型物件__proto__
的。
1 | var person = { |
創建兩個物件person
、john
,他們都有屬性firstname與lastname,只有person有方法getFullName
。
1 | john.__proto__ = person; |
將john的原型proto指向person,現在john的原型是person了
警告: 這樣的指向方式是為了讓人更好理解,在現實開發不可以用這樣的方式。
1 | console.log(john.getFullName()) //John Doe; |
我們可以得到 "John Doe"
的結果。原本在 john 的這個物件中,是沒有 getFullName()
這個方法的,但由於我讓john
物件的原型物件__proto__
變成了 person
這個物件,所以當 JavaScript 引擎在 john
物件裡面找不到getFullName()
這個方法時,它便會到__proto__
裡面去找,最後它找到了,於是它回傳 "John Doe"
的結果。
1 | console.log(john.firstName); //John |
我們會得到的是 John
而不是 'Default'
,因為 JavaScript 引擎在尋找 john.firstName
這個屬性時,在john
這個物件裡就可以找到了,因此它不會在往 __proto__
裡面找。這也就是剛剛在上面所的原型鍊(prototype chain)
的概念, 一旦它在上層的部分找到該屬性或方法時,就不會在往下層的proto去尋找。
在了解了prototype chain這樣的概念後,讓我們接著看下面這段程式碼:
1 | var jane ={ |
現在,你可以理解到會輸出什麼結果嗎?
答案是 "Jane Default"
。
因為在 jane
這個物件裡只有 firstName
這個屬性,所以當 JavaScript 引擎要尋找 getFullName()
這個方法和 lastName
這個屬性時,它都會去找 __proto__
裡面,而這裡面找到的就是一開始建立的person
這個物件的內容。
完整範例程式
1 | var person = { |