JS之路 Day03 - Prototype Chain(原型鏈)
昨天Prototype
世界的例子中,小白這種打電話讓別人再去打電話找的方式,其實就是一種Prototype Chain
的概念。
畫一張示意圖會比較清楚一些,用一個空的陣列來做舉例。
首先,陣列也是一種物件,空陣列代表裡面什麼都沒有,不過不代表不能從空陣列去獲取東西。
當我要從物件裡面找一個不存在的屬性,JavaScript 就會直接從它的原型物件去找,要是它的原型物件沒有,就會再往上找它的原型物件,直到最上面,都沒有那就是找不到。
而這個最上面的原型物件就稱作叫Object.prototype
。
真的就最上面了,再往上找就是null
。
而這一個由下往最上尋找的過程,一層一層相依尋找,就是原型鏈。
Object.create
這個可以拿來創造出一個擁有原型物件的新空物件,裡面的參數就帶要被繼承的原型物件,比如說:
1 | const food = { |
這個新被創造出來的物件其實原型物件就會自動綁定是 food 了,所以現在的 apple 也可以去看 eat 的這個屬性。
1 | console.log(apple.eat); //true |
除此之外,Object.create
還有一個很厲害的地方,它可以藉由放第二個參數,直接為新的物件增加額外的屬性。
1 | const food = { |
Object.getPrototypeOf()
昨天只講到如何設定原型,其實還可以獲取原型,那就是使用Object.getPrototypeOf()
,這個語法會回傳物件中的原型物件。
用上面的例子來舉例:
1 | const food = { |
apple 的原型物件就是 food,所以答案是 true,證明沒想錯。
__proto__
其實也能做到相同的事情,不過使用__proto__
來進行設定或是獲取都是件不太好的事情(詳情請看昨天),所以請記得,現在主流的設定跟獲取方式:
- Object.getPrototypeOf() => 獲取
- Object.setPrototypeOf() => 設定
但是__proto__
真的就一無可取之處嗎?那倒也不是,有一個用法的情境是不會被反對的。
__proto__
當作屬性時
圖片來源: MDN
看起來 MDN 裡面比較偏向說,不要使用. __proto__
的方式,但把__proto__
當作屬性時算是一個還蠻 OK 的方式。
也有查到其他地方也有這種說法:
The only usage of proto, that’s not frowned upon, is as a property when creating a new object: { proto: … }.
說法出處:https://javascript.info/prototype-methods
讓我們來看看這方法是怎麼實作的吧。
創建一個新物件時,可以把__proto__
當作是屬性,然後它的 value 直接設定成所想要的原型物件。
有一個重點是,__proto__
的值必須是物件或是 null,比如說值是字串就無法,其實蠻合理的,因為字串不能變成一個原型。
1 | const food = { |
在這邊有三個新創的物件,我利用__proto__
當屬性設定的方式,讓這三個物件形成一個原型鏈,藉由把 apple 的__proto__
設定成 food,再把 coconut 的__proto__
屬性設定成 apple,這樣一來,理論上應該可以在 coconut 使用到 food 的屬性,我們來測試看看。
1 | console.log(coconut.eat); //true |
看來結果沒有錯,確實已經藉由__proto__
當屬性的方式,把這三個物件串個原型鏈。
不要做的事情
現在已經提到了Object.setPrototypeOf
以及obj.__proto__
都可以直接的去更改原型物件,影響整體的Prototype Chain
,但這是一件好的事情嗎?
關於這點去研究後,發現有些文章會說這個行為其實不是一件很好的事情,如下:
圖片來源:https://javascript.info/prototype-methods
大意就是,如果你今天覺得,速度很重要,就不要去修改已經存在的原型了,昨天有提到說,物件只能指定一種原型物件,要是去修改它,就會去覆蓋掉原本的原型物件,而這種行為,照那篇文章說,會去破壞掉object property access operations
的內部優化,簡單說就會會讓javascript
的執行速度變得很慢。
除非修改的重要程度比運行的速度還要重要,不然通常在創建物件時,就只會設置那麼一次,就不會再修改了,確實設計上是想改就改,但是除非有很確定自己在做什麼,不然我的理解是避免去做這件事情。
總結
可能其它程式語言並不是如此,但在javascript
中就是透過Prototype Chain
來把上下關係給連接起來,藉此來達到繼承的方式,推薦大家可以去看 huli 的 該來理解 JavaScript 的原型鍊了,裡面講的很清楚好懂,那麼今天就是這些,我們明天見!