前兩天說明物件導向的三大特性及 JS 不符合物件導向,只能稱作支援物件導向而已,今天我們來看看 JS 的原型繼承。
首先我們先來看,什麼是原型:
兩個物件之間的原型關係 (prototype relationship) 跟繼承 (inheritance) 有關:
每個物件都可以有另一個物件作為它的原型 (prototype) , 如此一來,前者就會繼承其原型的所有特性。
一個物件藉由內部特性 (property)[[Prototype]]
來指定其原型。
每個物件都有這個特性,但它可能是null
。由[[Prototype]]
連接起來的物件串鏈 (chain of objects) 被稱作 原型串鏈 (prototype chain) 。
《Speaking JavaScript 第十七章-物件與繼承》
我相信第一次看到這段話的人肯定是滿臉問號,我也是一樣。
沒關係我們一步一步慢慢來看!
在 JS 中,我們可以很容易的建立一個物件,也可以輕易的修改和刪除已經指派給物件的屬性。
var obj = {
a : 'apple',
b : function(){},
c : {},
d : []
};
obj.a = [];
obj.b = "banana";
delete obj.c;
在開發過程中,我們都會盡量避免重複造輪子,也就是盡可能的重複利用現有的程式碼。
所以一種可重複利用的程式碼,並幫助我們把程式組織好的方式就是「繼承」,把存在於某個物件上的功能延伸到其他物件上。
在 JS 中,繼承就是用「原型」實作出來的。
const Jason = {
CSS : true
};
const Turtle = {
JS : true
};
const Kai = {
Vue : true
};
我們剛剛建立了三個物件,每個物件都有一個自己才能存取的屬性。
可以利用 in
測試物件能否存取某個屬性:
"JS" in Jason;//false
"CSS" in Jason;//true
如果我們想要 Json
存取 Turtle
的 JS
屬性,可以用 Object.setPrototypeOf
,需要兩個引數 (argument) ,並把第二個引數設為第一個引數的原型,所以我們可以這樣做:
Object.setPrototypeOf(Jason,Turtle);
"JS" in Jason;//true
現在我們可以透過 Jason
存取 JS
屬性了,這是因為每當像 Jason
存取 JS
時,便會在物件原型裡搜尋該屬性,因為我們已經讓 Jason
繼承了 Turtle
讓它成為其原型,所以可以存取 Turtle
的屬性。
在 JS 中,物件原型是物件的內部屬性,無法直接存取,所以會標示為 [[Prototype]]
。
那如果我們想要 Jason
也能存取 Kai
的屬性,相信很多人會直接這樣寫:
Object.setPrototypeOf(Jason,Kai);
"JS" in Jason;//false
"Vue" in Jason;//true
Jason
的確可以存取 Kai
的屬性了,但同時 Kai
也取代了 Turtle
成為了它的原型。
那我們該怎麼做呢?我們前面有提到過「每個物件都有 [[Prototype]]
特性可以來來指定其原型。」,所以 Turtle
成為 Jason
原型後,也可以指定 Kai
當作其原型,這個行為稱為「原型串鏈 (prototype chain) 」。
Object.setPrototypeOf(Jason,Turtle);
Object.setPrototypeOf(Turtle,Kai);
"JS" in Jason;//true
"Vue" in Jason;//true
可以看到說 Jason
已經可以順利存取 Turtle
及 Kai
的屬性了!
那麼,今天就到這邊,一樣如果有錯誤及來源未附上也歡迎留言指正,那麼我們明天見。
參考資料 :
忍者 JavaScript 開發技巧探秘
Speaking JavaScript