Javascript系列——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關鍵字建立的對象this

性能
在原型鏈上查找屬性比較耗時,對性能有反作用,這在性能要求苛刻的狀況下很重要。另外,試圖訪問不存在的屬性時會遍歷整個原型鏈。
遍歷對象的屬性時,原型鏈上的每一個可枚舉屬性都會被枚舉出來。要檢查對象是否具備本身定義的屬性,而不是其原型鏈上的某個屬性,則必須使用全部對象從 Object.prototype 繼承的 hasOwnProperty方法。
hasOwnProperty是 JavaScript 中處理屬性而且不會遍歷原型鏈的方法之一。(另外一種方法: Object.keys())
注意:檢查屬性是否 undefined還不夠。該屬性可能存在,但其值剛好設置爲 undefined。prototype

簡而言之, prototype 是用於類的,而 Object.getPrototypeOf() 是用於實例的(instances),二者功能一致。
[[Prototype]] 看起來就像遞歸引用code

//當你這樣建立一個對象時,具體是作了什麼呢
var o = new Foo();
//JavaScript 實際上執行的是:
var o = new Object();
o.__proto__ = Foo.prototype;
Foo.call(o);
相關文章
相關標籤/搜索