從__proto__和prototype來深入理解JS對(duì)象和原型鏈

原文:proto和prototype來深入理解JS對(duì)象和原型鏈

就標(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是什么?憑什么說onetwo的原型就是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ù)的prototypeobj.__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設(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é)

  1. 原型鏈的盡頭(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__()
  1. Function.prototypeFunction.__proto__為同一對(duì)象。
Function.prototype
? () { [native code] }

Function.__proto__
? () { [native code] }

  1. 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)生。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • JS中原型鏈,說簡單也簡單。 首先明確: 函數(shù)(Function)才有prototype屬性,對(duì)象(除Object...
    前小白閱讀 4,071評(píng)論 0 9
  • 〇、前言 一、JavaScript和Java在面向?qū)ο髾C(jī)制上的區(qū)別1、面向?qū)ο缶幊痰奶卣?、機(jī)制差異簡述 二、面向...
    馮阿良閱讀 3,409評(píng)論 0 29
  • 一些廢話 剛接觸js時(shí),都說原型鏈?zhǔn)莏s中最難的部分,看完教程,不以為然。直到一年多之后,仍然被問到很多答不上來和...
    金牛筆記閱讀 819評(píng)論 0 4
  • JS 中原型和原型鏈深入理解 首先要搞明白幾個(gè)概念: 函數(shù)(function) 函數(shù)對(duì)象(function obj...
    lueyoo閱讀 727評(píng)論 1 1
  • 宋先生,你怎么可以這么完美?你值得每個(gè)松果用心呵護(hù)真心愛!沒有你消息的日子 里我常常聽你唱的《真的》,一直單曲循環(huán)...
    歆曼閱讀 450評(píng)論 0 1

友情鏈接更多精彩內(nèi)容