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