就標(biāo)題而言,這是七八篇里起得最滿意的,高大上,即使外行人也會(huì)不明覺厲! :)
不過不是開玩笑,本文的確打算從__proto__和prototype這兩個(gè)容易混淆來理解JS的終極命題之一:對(duì)象與原型鏈。
proto和prototype
proto
引用《JavaScript權(quán)威指南》的一段描述:
Every JavaScript object has a second JavaScript object (or null ,
but this is rare) associated with it. This second object is known as a prototype, and the first object inherits properties from the prototype.
翻譯出來就是每個(gè)JS對(duì)象一定對(duì)應(yīng)一個(gè)原型對(duì)象,并從原型對(duì)象繼承屬性和方法。好啦,既然有這么一個(gè)原型對(duì)象,那么對(duì)象怎么和它對(duì)應(yīng)的?
對(duì)象proto屬性的值就是它所對(duì)應(yīng)的原型對(duì)象:
var one = {x: 1};
var two = new Object();
one.__proto__ === Object.prototype // true
two.__proto__ === Object.prototype // true
one.toString === one.__proto__.toString // true
上面的代碼應(yīng)該已經(jīng)足夠解釋清楚__proto__了。好吧,顯然還不夠,或者說帶來了新的問題:Object.prototype是什么?憑什么說one和two的原型就是Object.prototype?
prototype
首先來說說prototype屬性,不像每個(gè)對(duì)象都有__proto__屬性來標(biāo)識(shí)自己所繼承的原型,只有函數(shù)才有prototype屬性。
為什么只有函數(shù)才有prototype屬性?ES規(guī)范就這么定的。
開玩笑了,其實(shí)函數(shù)在JS中真的很特殊,是所謂的一等公民。JS不像其它面向?qū)ο蟮恼Z言,它沒有類(class,ES6引進(jìn)了這個(gè)關(guān)鍵字,但更多是語法糖)的概念。JS通過函數(shù)來模擬類。
當(dāng)你創(chuàng)建函數(shù)時(shí),JS會(huì)為這個(gè)函數(shù)自動(dòng)添加prototype屬性,值是空對(duì)象 值是一個(gè)有 constructor屬性的對(duì)象,不是空對(duì)象。而一旦你把這個(gè)函數(shù)當(dāng)作構(gòu)造函數(shù)(constructor)調(diào)用(即通過new關(guān)鍵字調(diào)用),那么JS就會(huì)幫你創(chuàng)建該構(gòu)造函數(shù)的實(shí)例,實(shí)例繼承構(gòu)造函數(shù)prototype的所有屬性和方法(實(shí)例通過設(shè)置自己的__proto__指向承構(gòu)造函數(shù)的prototype來實(shí)現(xiàn)這種繼承)。
小結(jié)
雖然對(duì)不熟悉的人來說還有點(diǎn)繞,但JS正是通過__proto__和prototype的合作實(shí)現(xiàn)了原型鏈,以及對(duì)象的繼承。
構(gòu)造函數(shù),通過prototype來存儲(chǔ)要共享的屬性和方法,也可以設(shè)置prototype指向現(xiàn)存的對(duì)象來繼承該對(duì)象。
對(duì)象的__proto__指向自己構(gòu)造函數(shù)的prototype。obj.__proto__.__proto__...的原型鏈由此產(chǎn)生,包括我們的操作符instanceof正是通過探測obj.__proto__.__proto__... === Constructor.prototype來驗(yàn)證obj是否是Constructor的實(shí)例。
回到開頭的代碼,two = new Object()中Object是構(gòu)造函數(shù),所以two.__proto__就是Object.prototype。至于one,ES規(guī)范定義對(duì)象字面量的原型就是Object.prototype。
更深一步的探討
我們知道JS是單繼承的,Object.prototype是原型鏈的頂端,所有對(duì)象從它繼承了包括toString等等方法和屬性。
Object本身是構(gòu)造函數(shù),繼承了Function.prototype;Function也是對(duì)象,繼承了Object.prototype。這里就有一個(gè)雞和蛋的問題:
Object instanceof Function // true
Function instanceof Object // true
什么情況下會(huì)出現(xiàn)雞和蛋的問題呢?就是聲明一個(gè)包含所有集合的集合??!好了,你們知道這是羅素悖論,但并不妨礙PL中這樣設(shè)計(jì)。
那么具體到JS,ES規(guī)范是怎么說的?
Function本身就是函數(shù),Function.__proto__是標(biāo)準(zhǔn)的內(nèi)置對(duì)象Function.prototype。
Function.prototype.__proto__是標(biāo)準(zhǔn)的內(nèi)置對(duì)象Object.prototype。
以上均翻譯自http://www.ecma-international.org/ecma-262/5.1/#sec-15,_雞和蛋_的問題就是這么出現(xiàn)和設(shè)計(jì)的:Function繼承Function本身,Function.prototype繼承Object.prototype。
一張圖和總結(jié)
[站外圖片上傳中...(image-7c1925-1562843139042)]
圖片來自 mollypages.org
相信經(jīng)過上面的詳細(xì)闡述,這張圖應(yīng)該一目了然了。
1. Function.prototype和Function.__proto__都指向Function.prototype,這就是雞和蛋的問題怎么出現(xiàn)的。
2. Object.prototype.__proto__ === null,說明原型鏈到Object.prototype終止。
其他總結(jié)
- 原型鏈的盡頭(
root)是Object.prototype。所有對(duì)象均從Object.prototype繼承屬性。
Object.prototype
{constructor: ?, __defineGetter__: ?, __defineSetter__: ?, hasOwnProperty: ?, __lookupGetter__: ?, …}
constructor: ? Object()
hasOwnProperty: ? hasOwnProperty()
isPrototypeOf: ? isPrototypeOf()
propertyIsEnumerable: ? propertyIsEnumerable()
toLocaleString: ? toLocaleString()
toString: ? toString()
valueOf: ? valueOf()
__defineGetter__: ? __defineGetter__()
__defineSetter__: ? __defineSetter__()
__lookupGetter__: ? __lookupGetter__()
__lookupSetter__: ? __lookupSetter__()
get __proto__: ? __proto__()
set __proto__: ? __proto__()
-
Function.prototype和Function.__proto__為同一對(duì)象。
Function.prototype
? () { [native code] }
Function.__proto__
? () { [native code] }
-
Function.prototype直接繼承root(Object.prototype)。
Function.prototype.__proto__ === Object.prototype
true
Function.prototype instanceof Object
true
Function.prototype instanceof Function
false
Function instanceof Function
true
Array instanceof Function
true
Object instanceof Function
true
Function instanceof Object
true
最后總結(jié):先有Object.prototype(原型鏈頂端),F(xiàn)unction.prototype繼承Object.prototype而產(chǎn)生,最后,F(xiàn)unction和Object和其它構(gòu)造函數(shù)繼承Function.prototype而產(chǎn)生。