prototype與__proto__的區別
對於有基於類的語言經驗 (如 Java 或 C++) 的開發人員來說,JavaScript 有點令人困惑,因為它是動態的,並且本身不提供一個 class 實現。(在 ES2015/ES6 中引入了 class 關鍵字,但只是語法糖,JavaScript 仍然是基於原型的)。
當談到繼承時,JavaScript 只有一種結構:物件。每個例項物件( object )都有一個私有屬性(稱之為proto )指向它的原型物件( prototype )。該原型物件也有一個自己的原型物件(proto ) ,層層向上直到一個物件的原型物件為 null。根據定義,null 沒有原型,並作為這個原型鏈中的最後一個環節。
幾乎所有 JavaScript 中的物件都是位於原型鏈頂端的 Object的例項。
儘管這種原型繼承通常被認為是 JavaScript 的弱點之一,但是原型繼承模型本身實際上比經典模型更強大。例如,在原型模型的基礎上構建經典模型相當簡單。
繼承屬性
JavaScript 物件是動態的屬性“包”(指其自己的屬性)。JavaScript 物件有一個指向一個原型物件的鏈。當試圖訪問一個物件的屬性時,它不僅僅在該物件上搜尋,還會搜尋該物件的原型,以及該物件的原型的原型,依次層層向上搜尋,直到找到一個名字匹配的屬性或到達原型鏈的末尾。
遵循ECMAScript標準,someObject.[[Prototype]] 符號是用於指向 someObject的原型。從 ECMAScript 6 開始,[[Prototype]] 可以通過 Object.getPrototypeOf()和 Object.setPrototypeOf()訪問器來訪問。這個等同於 JavaScript 的非標準但許多瀏覽器實現的屬性 __proto__。
但它不應該與建構函式 func 的 prototype 屬性相混淆。被建構函式建立的例項物件的 [[prototype]] 指向 func 的 prototype 屬性。Object.prototype 屬性表示 Object的原型物件。
繼承方法
JavaScript 並沒有其他基於類的語言所定義的“方法”。在 JavaScript 裡,任何函式都可以新增到物件上作為物件的屬性。函式的繼承與其他的屬性繼承沒有差別,包括上面的“屬性遮蔽”(這種情況相當於其他語言的方法重寫)。
當繼承的函式被呼叫時,this 指向的是當前繼承的物件,而不是繼承的函式所在的原型物件。
建立物件的方式
語法結構建立的物件
構造器建立的物件
Object.create建立的物件
class關鍵字建立的物件
效能
在原型鏈上查詢屬性比較耗時,對效能有副作用,這在效能要求苛刻的情況下很重要。另外,試圖訪問不存在的屬性時會遍歷整個原型鏈。
遍歷物件的屬性時,原型鏈上的每個可列舉屬性都會被枚舉出來。要檢查物件是否具有自己定義的屬性,而不是其原型鏈上的某個屬性,則必須使用所有物件從 Object.prototype 繼承的 hasOwnProperty方法。
hasOwnProperty是 JavaScript 中處理屬性並且不會遍歷原型鏈的方法之一。(另一種方法: Object.keys())
注意:檢查屬性是否 undefined還不夠。該屬性可能存在,但其值恰好設定為 undefined。
簡而言之, prototype 是用於類的,而 Object.getPrototypeOf() 是用於例項的(instances),兩者功能一致。
[[Prototype]] 看起來就像遞迴引用
//當你這樣建立一個物件時,具體是做了什麼呢 var o = new Foo(); //JavaScript 實際上執行的是: var o = new Object(); o.__proto__ = Foo.prototype; Foo.call(o);