javascript原型鏈(prototype)之思考脈絡

初學者的JS繼承觀念心得

Javascript中,任何不是string、number、boolean、null或undefined的值都是物件,物件是可變的,而且是通過參考(by reference)來操作,並非值(by value)。

函數在JS中很特殊,是所謂的_一等公民_(First class),JS不像其它物件導向的語言,它沒有class的概念,JS通過函數來建構實體,通常稱此函數為建構函數,函數在JS中可以視為物件,一種可以用來呼叫的物件。

Javascript 是 prototype-base-oriented 的語言,而且Javascript 中的 class 與C#或JAVA的意思不相同,千萬不要嘗試用傳統物件導向的Class思考方式來學習Javascript的繼承,[[prototype]] 、__proto__ 、.prototype三者要搞清楚。

JavaScript 中有四種建立函數的方式請參考[1],這邊只談使用關鍵字function 來建立函數,JavaScript中以關鍵字function建立了數個內建的函數e.g. Object()、Number()、String()、Function()…等。

每個函數有一個名為prototype的屬性,如果你想問為什麼需要這個屬性你可以參考[2],當你創建函數時,JS會為函數自動添加prototype屬性,以內建函數Object()來說,他當然也有prototype屬性,於是可以得到一個Object.prototype的物件,如figure.1,建議看完參考資料[3]

figure.1

Object.prototype又有各種屬性如:toString()自動轉型時發揮作用、valueOf()用來取值、constructor()用來指回原本自己...等等其他屬性。

現在使用關鍵字function建立Foo函數,可得figure.2,Foo.prototype是個物件,此物件有個內部屬性[[prototype]]或稱__proto__指向Object.prototype,這就是JS中的原型鏈,Object.prototype.__proto__則指向null也就是原型鏈的終點。

figure.2

利用Foo函數當作建構函數來創造實體,這邊使用new來創造實體物件,物件不會有prototype屬性,但是每個物件有一個名為[[prototype]]的屬性,物件的__proto__會指向,建構函數的prototype物件,最後可得figure.3。

此[[prototype]]屬性也被稱為__proto__,__proto__在ES6時被定為正式屬性,[[prototype]]或__proto__所連接的物件,就是所稱的原型鏈。

呼叫a.whoAmI(),發現a沒有whoAmI,利用__proto__尋找原型鏈中的物件,發現 Foo.prototype存在whoAmI的屬性,最後執行成功。

呼叫a.say()發現a沒有say的屬性,而原型鏈上也沒有,say只存在於b物件,a的原型鏈無法追蹤到,最後得到 TypeError。

figure.3
原型鏈關係圖 from mollypages.org

[1]定義 JavaScript 函數(Functions)的各種方式

[2]Javascript继承机制的设计思想

[3]JavaScript Prototype Explained By Examples

[4]Javascripter 必須知道的繼承 prototype, [[prototype]], __proto__

物件的生成目前有兩個方式,new跟 Object.create(),運作方式如下,最大的差別在於this綁定建構函數的部分,Object.create()會失去this綁定,失去建構函式,new不會失去,this綁定4種方式的資料可參考[5]

new 的運作
Object.create()的運作

[5]重新認識 JavaScript: Day 20 What’s “THIS” in JavaScript

--

--

Gopher is cute
Caesar's study review on Web development

我的第一份後端工作結束了,短短四個月,部門全員掰掰,尋找新的機會。