__proto__
每個JS對象一定對應一個原型對象,并從原型對象繼承屬性和方法
對象proto屬性的值就是它所對應的原型對象。
prototype
ES規范規定:每個對象都有proto屬性來標識自己所繼承的原型,只有函數才有prototype屬性。
當你創建函數時,JS會為這個函數自動添加prototype屬性,值是空對象。而一旦你把這個函數當作構造函數(constructor)調用(即通過new關鍵字調用),那么JS就會幫你創建該構造函數的實例,實例繼承構造函數prototype的所有屬性和方法(實例通過設置自己的proto指向承構造函數的prototype來實現這種繼承)。
原型鏈
JS通過proto和prototype的合作實現了原型鏈,以及對象的繼承。
構造函數,通過prototype來存儲要共享的屬性和方法,也可以設置prototype指向現存的對象來繼承該對象。
對象的proto指向自己構造函數的prototype。obj.__proto__.__proto__...的原型鏈由此產生,包括我們的操作符instanceof正是通過探測obj.__proto__.__proto__... === Constructor.prototype來驗證obj是否是Constructor的實例。
Function.prototype
Function.prototype是個不同于一般函數(對象)的函數(對象)。
- Function.prototype像普通函數一樣可以調用,但總是返回undefined。
- 普通函數實際上是Function的實例,即普通函數繼承于Function.prototype。func.__proto__ === Function.prototype。
- Function.prototype繼承于Object.prototype,并且沒有prototype這個屬性。func.prototype是普通對象。
- Function.prototype其實是個另類的函數,可以獨立于/先于Function產生。
Function.prototype 為什么是函數?
Array.prototype 是 Array 類型,Map.prototype 是 Map 類型,Set.prototsype 是 Set 類型
所以,為了保持一致性,Function.prototype 也應該是 Function 類型
結論
/**
* 實例的 __proto__ 指向構造函數的 prototype
**/
function A(){...} // 創建A函數時js同時自動生成A.prototype
A.__proto__ === Function.prototype // A是通過 Function 構造函數生成的
A.prototype.__proto__ === Object.prototype // 原型對象是通過 Object 構造函數生成的
Object.prototype.__proto__ === null //ES規范規定
Function.prototype.__proto__ === Object.prototype // ES規范規定
Object.__proto__ === Function.prototype // Object構函數是通過 Function 構造函數生成的
Function.__proto__ === Function.prototype // Function構函數是通過 Function 構造函數生成的
var a = new A() // new生成實例
a.__proto__ === A.prototype // new實例通過設置自己的__proto__指向承構造函數的prototype來實現這種繼承
A.prototype.constructor = A // 原型的構造器
a.constructor === A // 當獲取 a.constructor 時,其實 a 中并沒有 constructor 屬性,當不能讀取到constructor 屬性時,會從 a 的原型也就是 A.prototype 中讀取,正好原型中有該屬性