前言
我們用上一節的範例來解釋什麼是 原型 (prototype) 與 原型鏈 (prototype chain),然後也不難發現 JavaScript 很多底層的觀念都是鏈狀的。
回顧上一節的 ES5 範例
1 | function dog(name){ |
上一節的內容中,我們知道只要於 dog.prototype
上新增方法,這樣之後透過 new
產生的 dog
物件都具有 sayHello
方法。
以這個例子來說,當我們印出 d
的內容時,可以發現當中有個隱藏的屬性「.__proto__
」,也發現寫在 dog.prototype
上的方法在這邊出現了。
甚至會發現怎麼點開了「.proto」裡面還有一個「.proto」?
「.proto」
以這個例子白話的說,意思就是當在變數 d
指向的物件身上找不到對應的方法時,便從「.__proto__
」內尋找有沒有對應的方法,聽起來是不是跟之前解釋的 ScopeChain 有點像呢。
1 | ... 省略 ... |
而使用三等號來比較的話,可以得知兩者是一樣的。
當呼叫 d.sayHello()
的過程
1 | ... 省略 ... |
- 檢查變數
d
指向的物件屬性內有沒有sayHello
? 這個例子沒有。 - 接著找該物件的
__proto__
屬性內有沒有sayHello
,若有就停止不會繼續往後找下去。
以這個例子來說,在步驟二時就找到了,那如果沒有的情況呢?
還記得上面的一張圖嗎?
怎麼點開了「
.__proto__
」裡面還有一個「.__proto__
」?
- 如果還是找不到,便繼續往
.__proto__
尋找,如d.__proto__.__proto__
此時先抽離這個步驟,從這邊可以觀察出「.__proto__
」是一層一層的,而越後面的「.__proto__
」就越接近「底」的部分。
而本例中 dog 物件的「.__proto__
」下一層就是原始物件的 prototype
,所以下面程式碼為 true
1 | console.log(d.__proto__.__proto__ === Object.prototype); // true |
如何知道是不是已經找到底層了呢?只要輸出為 null
代表上一層就是頂層了。
1 | console.log(d.__proto__.__proto__.__proto__); // null |
回到剛才的步驟
- 如果還是找不到,便會繼續往
.__proto__
尋找,直到找到為止 - 如果找到底層了還是沒有,則跳出錯誤訊息。
1
2
3
4
5
6
7
8
9
10function dog(name){
this.name = name;
}
dog.prototype.getName = function(){
return this.name;
}
var d = new dog('abc');
d.sayHello();
整理一下對應的關聯
可以得知這個過程是一層一層逐漸地往下找的,而這個過程被稱之為原型鏈 (prototype chain),其實跟範圍鏈 (scope chain) 有點相似。
這邊也順手觀察一下 dog.__proto__
是什麼
1 | console.log(dog.__proto__); // ƒ () { \[native code\] } |
因為 dog 本身就是一個函式,出現這樣子是符合預期的。
觀察至此,大部分的疑惑都解開,只剩下
new
了,接下來會提到new
到底做了些什麼。
知道了這些可以做什麼?
可以進行一些比較有趣的事情,像是我們可以在 String 的原型上增加一個自己寫的方法,這樣之後只要型別屬於 String 就可以使用。
1 | String.prototype.getFirst = function(){ |