JS之路 Day02 - Prototype (原型)
前言
我相信每個語法的發明都有它意義存在,以我看來Prototype
的存在,是為了要讓JavaScript
也能實現物件導向,準確點來說,可以用來做到物件的繼承,透過Prototype
的方式。
Prototype 的產生是為了 JavaScript 的繼承
而所謂物件的繼承,就是可以從其他地方拿到本身沒有的方法或是屬性,藉由去繼承有方法跟屬性的物件,來獲得使用那些方法跟屬性的權利。
在JavaScript
中,每個object
都會去連結到Prototype object
=> 每個object
都可以看得到裡面有Prototype
(null
是例外~)。
這個Prototype
是[[prototype]]
也是原型物件。
舉例來說,創造一個空物件,會看見[[Prototype]]:
但是用Object.create
使用null
當原型的這個新物件是會看到no properties
,很乾淨的那種。
Prototype object 包含了 properties(屬性) 跟 methods(方法)
簡單解釋一下什麼是properties
跟methods
。
首先要先知道物件會有key-value pairs
,接著看下方程式碼:
1 | const fruit = { |
value
是一般值 => properties
value
是一個函式 => methods
意思是連結到這個Prototype
的所有 object
,都可以去使用這個Prototype object
的properties
以及methods
,這就是Prototype
的奇妙之處,我沒有但我可以還是用。
再說一次,這種我沒有,但是我還是可以用的行為,其實是一種繼承的概念,因為是用原型來達成的,所以會把它稱為原型繼承。
如何設定Prototype
為什麼要設定,要如何設定,讓我用一個例子來舉例:
Prototype
世界的小白家有餅乾,小紅家有花朵,他們住在不同的地方,要去拿到小白的餅乾,就沒辦法拿到小紅的花朵,反之也是一樣。
想要從小白那邊獲得餅乾,又能拿到花朵,這時候需要電話,比如説小白吃著餅乾,又想要手上拿著花朵,但這時候小白家沒有花朵,但是可以打電話給小紅,叫她把花給速速送過來。
這個電話號碼其實就是Object.setPrototypeOf
。
這個語法會需要兩個參數,如下用 A 跟 B 來表示:
1 | Object.setPrototypeOf(A, B) |
第一個參數 A 代表的是要設定原型的物件,第二個參數 B 代表的是參數 A 的新原型物件。
所以在這邊可以藉由小白獲得了小紅的電話號碼,獲得可以拿到小紅花朵這個概念,拿來比擬小白透過 Object.setPrototypeOf() 將「小紅指定為原型」。
用這個例子的範例來說,程式會長這樣:
1 | // 小白擁有很多餅乾 |
在上面的程式裡面,小白就是物件,小紅就是原型物件,小白可以指定原型物件(打電話),來獲得原型物件的東西。
那假如我今天又想要在小白家拿到披薩呢? 現在知道說,小綠家有很多披薩,一樣把他的電話新增到小白手機裡,讓小白打電話給他。
1 | const white = { cookies: true }; |
小白家拿不到小紅的花朵,其實是因為Prototype
世界的電話,一次只能存取一個電話號碼,小白手機裡面原本是存小紅電話號碼,但後來新增了小綠電話號碼,小紅的就消失了。
小白沒有在記別人電話號碼的,手機裡號碼不見了就不見了,現在只剩下小綠電話號碼,所以只能從小白家獲取小綠披薩。
重點 : 物件只能指定一種原型物件。
小白苦思著,竟然一次只能存取一個電話號碼,突然,小白靈光乍現,他想到了,只要叫小綠去用他的手機存取小紅電話不就好了嗎?
他打電話給小綠,小綠再打電話給小紅,之後再全部拿到小白家。
1 | const white = { cookies: true }; |
透過這個友善的故事,希望能讓大家對於原型這個概念有個初步的理解,另外我還有在學習時看到一些不錯的比喻,寫得很棒歡迎去看 :
- 藉由洛克人打贏 boss 就會獲得 boss 新武器的機制來做原型的比喻。
來自於重新認識 JavaScript: Day 24 物件與原型鏈 - 藉由猜謎問問題,遇到不會的問題就問其他人,來做原型的比喻。
來自於忍者:JavaScript 開發技巧探秘, 2/e
設定Prototype
其他方式
除了setPrototypeOf()
之外其實也有一個方法可以去設定原型物件,就是__proto__
,一樣可以讓小白拿到小紅的花:
1 | const white = { cookies: true }; |
雖然它可以也可以拿來設定,不過我們一般不會使用它,因為它已經被認為過時以及超級不推薦使用,現在都會建議使用setPrototypeOf()
,我會發現是因為研究時 MDN 上看到,要是你在 MDN 上面有查過__proto__
這個語法的話,你會發現點開映入眼簾的是很多的警告,很可怕,有興趣可以研究。
圖片來源:MDN
構造函式的原型
我快寫不完,最後簡單介紹一下構造函式的原型,構造函式創個一個新的物件時,這個新的物件會被設定原型,原型物件就是構造函式,可能有些人不知道什麼是構造函式,但今天再詳細解釋會有點太長,所以關於這個以後會專門做一期文章給大家講解。
簡單一個小範例介紹:
1 | function Food(name) { |
Food 就是建構函式,然後藉由 new 去實例出 apple,這個新創造出來的物件,會被存到這個變數 apple 裡面。
然後這個 apple 它的原型物件就會是 Food,我們可以來驗證一下這件事情。
1 | console.log(apple.__proto__ === Food.prototype); // true |
結果沒錯,相當於就是這個新的物件會繼承構造函式的原型。
明天會繼續接著講原型鏈的
reference
[1] MDN - Object prototypes
[2] W3Schools - JavaScript Object Prototypes
[3] JS 原力覺醒 Day21 - 原型