摘要:由于一般所有的原型鏈最終都會(huì)指向頂端的,所以它們都是的。好了現(xiàn)在了,成了所有對(duì)象原型鏈的。
JavaScript里任何東西都是對(duì)象,任何一個(gè)對(duì)象內(nèi)部都有另一個(gè)對(duì)象叫__proto__,即原型,它可以包含任何東西讓對(duì)象繼承。當(dāng)然__proto__本身也是一個(gè)對(duì)象,它自己也有自己的__proto__,這樣一級(jí)一級(jí)向上,就構(gòu)成了一個(gè)__proto__鏈,即原型鏈。當(dāng)然原型鏈不會(huì)無(wú)限向上,它有個(gè)終點(diǎn),可以稱(chēng)為原型鏈的頂端,或者root,它是一個(gè)特殊的對(duì)象,它的__proto__為null。
obj.__proto__.__proto__......__proto__ === null;
但是對(duì)象并不是憑空產(chǎn)生的,它一般是某一個(gè)class,或者確切說(shuō)是構(gòu)造函數(shù)的實(shí)例。JavaScript和其它面向?qū)ο蟮恼Z(yǔ)言不太一樣,它沒(méi)有所謂的class,而是用函數(shù)來(lái)模擬class。定義了一個(gè)函數(shù),實(shí)際上就定義了一個(gè)class,函數(shù)本身就是class的constructor,例如:
function foo() {} var a = new foo();
這里創(chuàng)建了一個(gè)對(duì)象a,是foo的一個(gè)實(shí)例。既然a是一個(gè)對(duì)象,它就有原型__proto__,那a的__proto__是怎么設(shè)定的?這里就引出了prototype,它是函數(shù)才有的一個(gè)屬性,也是一個(gè)對(duì)象,一個(gè)函數(shù)創(chuàng)建的實(shí)例的__proto__就指向這個(gè)函數(shù)的prototype。所以a的__proto__被設(shè)定為foo.prototype,即:
a.__proto__ === foo.prototype;
當(dāng)然foo.prototype也是個(gè)對(duì)象,它自然也有__proto__,默認(rèn)指向Object.prototype,即:
foo.prototype.__proto__ === Object.prototype;
而Object.prototype已經(jīng)沒(méi)有原型了,它的__proto__是null。這是JavaScript里一個(gè)很特殊的對(duì)象,它就是原型鏈的頂端。
以上就構(gòu)成了由對(duì)象a開(kāi)始的原型鏈:
a.__proto__ === foo.prototype; a.__proto__.__proto__ === Object.prototype;
JavaScript因此而推斷出:
a instanceof foo === true; a instanceof Object === true;
注意,這就是JavaScript判斷一個(gè)對(duì)象是否instanceof某個(gè)函數(shù)的依據(jù),即對(duì)象a的原型鏈上有沒(méi)有一個(gè)__proto__是這個(gè)函數(shù)的prototype,如果有,那么a就是這個(gè)函數(shù)的instance。由于一般所有的原型鏈最終都會(huì)指向頂端的Object.prototype,所以它們都是Object的instance。
prototype和__proto__有什么作用我們可以設(shè)定
foo.prototype = { x : 2, y : 3, }
這里用字面量創(chuàng)建一個(gè)對(duì)象賦給了foo.prototype,這樣foo所創(chuàng)建出來(lái)的任何對(duì)象的__proto__就都指向了它,因此它們就可以訪問(wèn)里面的field,或者說(shuō)可以認(rèn)為它們繼承了這些field,例如:
var a = new foo(); a.x === 2; a.y === 3;
不只是foo.prototype,繼續(xù)往上foo.prototype的__proto__,以及原型鏈上所有的__proto__都會(huì)被這個(gè)對(duì)象繼承。一般的對(duì)象的很多常用方法如toString,都是從原型鏈頂端的Object.prototype里繼承來(lái)的。
函數(shù)也是對(duì)象上面說(shuō)了,JavaScript里任何東西都是對(duì)象,包括函數(shù),可以稱(chēng)為函數(shù)對(duì)象。所以foo也是對(duì)象,那foo的原型__proto__是什么?它是誰(shuí)的instance?
JavaScript里定義了一個(gè)特殊的函數(shù)叫Function,可以稱(chēng)作是所有函數(shù)的爸爸,所有的函數(shù)都是它的實(shí)例,因此你可以認(rèn)為,定義foo的時(shí)候發(fā)生了這樣的事情:
var foo = new Function(args, function_body);
于是我們有:
foo.__proto__ === Function.prototype; foo instanceof Function === true;
注意這里的Function.prototype,這也是JavaScript里一個(gè)特殊的對(duì)象,Chrome的console里要是輸入Function.prototype,根本什么也打印不出來(lái),什么native code,就是說(shuō)它是內(nèi)部實(shí)現(xiàn)的。
這個(gè)原型鏈還沒(méi)到頂,F(xiàn)unction.prototype仍然有原型__proto__,指向Object.prototype,所以我們最終有:
foo.__proto__.__proto__ === Object.prototype; foo instanceof Object === true;
現(xiàn)在有個(gè)問(wèn)題來(lái)了,那Function自己呢?它其實(shí)也是個(gè)函數(shù),也是個(gè)對(duì)象,它的__proto__指向誰(shuí)?答案是它自己的prototype,即:
Function.__proto__ === Function.prototype; Function instanceof Function === true; Function.__proto__.__proto__ === Object.prototype; Function instanceof Object === true;
總結(jié)一下:所有的函數(shù)都是Function的instance,F(xiàn)unction自己也是它自己的instance,不過(guò)后者嚴(yán)格來(lái)說(shuō)并不準(zhǔn)確,F(xiàn)unction并不是它自己創(chuàng)造自己的,而應(yīng)該看作JavaScript里原生的一個(gè)函數(shù)對(duì)象,只不過(guò)它的__proto__指向了它自己的prototype而已。
Function和Object的關(guān)系這是JavaScript比較奇葩的一個(gè)地方,也是不太讓人容易接受的一點(diǎn)。
我們知道一般任何對(duì)象都是Object的instance,因?yàn)樵玩湹捻敹硕贾赶騉bject.prototype。那么Object本身是什么?Object也是個(gè)函數(shù),而任何函數(shù)都是Function的實(shí)例對(duì)象,比如Array,String,當(dāng)然Object也包括在內(nèi),它也是Function的實(shí)例,即:
Object.__proto__ === Function.prototype; Object instanceof Function === true
同時(shí),F(xiàn)unction是個(gè)對(duì)象,它的原型是Function.__proto__,指向Function.prototype,并且這個(gè)原型鏈向上繼續(xù)指向Object.prototype,即:
Function.__proto__.__proto__ === Object.prototype; Function instanceof Object === true
這樣就有了一個(gè)JavaScript里經(jīng)常說(shuō)到的蛋雞問(wèn)題:
Object instanceof Function === true Function instanceof Object === true
到底誰(shuí)先誰(shuí)后,誰(shuí)主誰(shuí)次?關(guān)于這一點(diǎn)網(wǎng)上已經(jīng)有很多解釋?zhuān)@里首先陳述我的觀點(diǎn),是先有Function,它是主;后有Object,是次。以下是我個(gè)人的理解,可能并不準(zhǔn)確。要看待這個(gè)問(wèn)題,我們可以從JavaScript創(chuàng)造世界開(kāi)始想象:
我們知道Object.prototype是原型鏈的root。但首先,現(xiàn)在世界上還沒(méi)有Object,更沒(méi)有Object.prototype?,F(xiàn)在只有個(gè)特殊的對(duì)象,姑且稱(chēng)它為root_prototype,里面定義了些基本的field和method比如toString之類(lèi)的,以后我們要讓所有的原型鏈都最終指向它。注意它沒(méi)有原型,它的__proto__是null,這也是它和所有其它JavaScript對(duì)象的區(qū)別,使它與眾不同,能有資格成為原型鏈的root。
然后定義Function。先看Function的prototype,我們只要知道這是一個(gè)特殊的對(duì)象,它的原型__proto__指向剛才的root_prototype,就是說(shuō)Function.prototype.__proto__ === root_prototype,這樣它就算連上了原型鏈的root。
上面已經(jīng)講過(guò)了,F(xiàn)unction也是個(gè)對(duì)象,也有__proto__,指向Function自己的prototype,所以說(shuō)白了Function也是個(gè)奇葩,是JavaScript里規(guī)定的一個(gè)特殊的東西。而Function.prototype的原型__proto__繼續(xù)指向root_prototype,所以Function也連上了原型鏈root。
所有的函數(shù),什么Array之類(lèi)的,包括Object也是函數(shù),都是繼承Function的,就是說(shuō),任意函數(shù)foo.__proto__ === Function.prototype,所以我們自然有Object instanceof Function。
然后再看Object,它本來(lái)就是個(gè)函數(shù)而已,和其它函數(shù)沒(méi)什么區(qū)別,都繼承了Function??墒乾F(xiàn)在最關(guān)鍵的一步是,強(qiáng)行設(shè)定讓Object.prototype = root_prototype,這樣Object.prototype就成了原型鏈的root!注意這里的邏輯,是先有了root_prototype,然后規(guī)定Object.prototype等于它,這一步是人為規(guī)定的,這就是Object的特殊之處。你要是規(guī)定bar.prototype也等于root_prototype,那bar.prototype也成了原型鏈的的頂端。所以JavaScript里__proto__這個(gè)東西其實(shí)是很隨意的,放在哪個(gè)函數(shù)的prototype里,哪個(gè)函數(shù)就成了你爹。
好了現(xiàn)在Object.prototype === root_prototype了,成了所有對(duì)象原型鏈的root。那么由第3步的結(jié)論,F(xiàn)unction也是對(duì)象,是連上了root_prototype的,而現(xiàn)在root_prototype給Object.prototype了,那Function自然就是instanceof Object。
- 首先沒(méi)雞沒(méi)蛋,先有一個(gè)特殊對(duì)象root_prototype,它是上帝。 - 接下來(lái)應(yīng)該是先有Function,并且定義它的prototype和__proto__,都連上了root_prototype。 - 然后才有了Object,它是Function的instance,繼承了Function。這時(shí)候Object仍然只是個(gè)普通的函數(shù)。 - 然后規(guī)定Object.prototype = root_prototype,這時(shí)候Object才開(kāi)始顯得特殊,成為了原型鏈的頂端,否則它和其它函數(shù)根本沒(méi)什么區(qū)別。 - 于是所有的東西,包括Function,都成了Object的instance了。
這里要強(qiáng)調(diào)Object和其它函數(shù)的不同之處。Object之所以特殊,就是因?yàn)镺bject的prototype被設(shè)定為了root_prototype,僅此而已;而其它函數(shù)例如foo,它的prototype只是一個(gè)普通的對(duì)象,這個(gè)對(duì)象的__proto__默認(rèn)情況下指向root_prototype。至于為什么這樣設(shè)定,為什么Object會(huì)特殊化,大概只是因?yàn)镺bject這個(gè)名字起得好,而foo,bar沒(méi)那么特殊。所以說(shuō)白了Object函數(shù)只是一個(gè)盛放root_prototype的容器而已,從而使它晉升為一個(gè)特殊的函數(shù)。
另外值得注意的是,obj instanceof function 并不意味著obj就是這個(gè)function創(chuàng)建出來(lái)的,只不過(guò)是obj的原型鏈上有function.prototype而已。
所以所謂的 Object instanceof Function 和 Function instanceof Object 的蛋雞問(wèn)題,前者應(yīng)該來(lái)說(shuō)是自然而然、不容置疑的,可以認(rèn)為Object函數(shù)是Function創(chuàng)造出來(lái)的;而后者說(shuō)白了只是因?yàn)閺?qiáng)行規(guī)定了Object函數(shù)的特殊性,而導(dǎo)致的一個(gè)推論,而Function并非是Object創(chuàng)建的。
當(dāng)然這些概念繞來(lái)繞去討論其實(shí)我感覺(jué)沒(méi)什么很大意義,無(wú)非只是自圓其說(shuō)而已。大家寫(xiě)代碼時(shí)也不會(huì)太糾結(jié)這些問(wèn)題,重點(diǎn)還是要把原型鏈搞清楚。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/90361.html
摘要:而作為構(gòu)造函數(shù),需要有個(gè)屬性用來(lái)作為以該構(gòu)造函數(shù)創(chuàng)造的實(shí)例的繼承。 歡迎來(lái)我的博客閱讀:「JavaScript 原型中的哲學(xué)思想」 記得當(dāng)年初試前端的時(shí)候,學(xué)習(xí)JavaScript過(guò)程中,原型問(wèn)題一直讓我疑惑許久,那時(shí)候捧著那本著名的紅皮書(shū),看到有關(guān)原型的講解時(shí),總是心存疑慮。 當(dāng)在JavaScript世界中走過(guò)不少旅程之后,再次萌發(fā)起研究這部分知識(shí)的欲望,翻閱了不少書(shū)籍和資料,才搞懂...
摘要:對(duì)應(yīng)的關(guān)系圖如下講解了構(gòu)造函數(shù)和原型對(duì)象之間的關(guān)系,那么實(shí)例對(duì)象和原型對(duì)象之間的關(guān)系又是怎么樣的呢下面講解。原型對(duì)象的指向的是構(gòu)造函數(shù)和本身沒(méi)有屬性,但是其原型對(duì)象有該屬性,因此也能獲取到構(gòu)造函數(shù)。 JavaScript進(jìn)階 - 1. 原型和原型鏈的概念 我們好多經(jīng)常會(huì)被問(wèn)道JavaScript原型和原型鏈的概念,還有關(guān)于繼承,new操作符相關(guān)的概念。本文就專(zhuān)門(mén)整理了原型和原型鏈的概念...
摘要:如果要理清原型和原型鏈的關(guān)系,首先要明確一下幾個(gè)概念中的所有東西都是對(duì)象,函數(shù)也是對(duì)象而且是一種特殊的對(duì)象中所有的東西都由衍生而來(lái)即所有東西原型鏈的終點(diǎn)指向?qū)ο蠖加幸粋€(gè)隱藏的屬性,他指向創(chuàng)建它的構(gòu)造函數(shù)的原型,但是有一個(gè)例外,指向的是。 首先要搞明白幾個(gè)概念: 函數(shù)(function) 函數(shù)對(duì)象(function object) 本地對(duì)象(native object) 內(nèi)置對(duì)象(bu...
摘要:而和的存在就是為了建立這種子類(lèi)與父類(lèi)間的聯(lián)系。創(chuàng)建一個(gè)基本對(duì)象建立新對(duì)象與原型我把它理解為類(lèi)之間的連接執(zhí)行構(gòu)造函數(shù)小結(jié)可以理解為類(lèi),也就是存儲(chǔ)一類(lèi)事物的基本信息。原型原型鏈和繼承之間的關(guān)系。 原型 原型的背景 首先,你應(yīng)該知道javascript是一門(mén)面向?qū)ο笳Z(yǔ)言。 是對(duì)象,就具有繼承性。 繼承性,就是子類(lèi)自動(dòng)共享父類(lèi)的數(shù)據(jù)結(jié)構(gòu)和方法機(jī)制。 而prototype 和 __proto__...
摘要:在中,主要有兩種創(chuàng)建對(duì)象的方法分別是對(duì)象字面量以及調(diào)用構(gòu)造函數(shù)對(duì)象字面量調(diào)用構(gòu)造函數(shù)其實(shí)上述兩種創(chuàng)建對(duì)象的方法,本質(zhì)上是一樣的,都是引擎調(diào)用對(duì)象的構(gòu)造函數(shù)來(lái)新建出一個(gè)對(duì)象。 原型與原型鏈?zhǔn)莏avascript里面最最核心的內(nèi)容,如果不能理解它們之間的存在關(guān)系的話(huà),那么我們是不能理解這門(mén)語(yǔ)言的。 在JS中,主要有兩種創(chuàng)建對(duì)象的方法, 分別是對(duì)象字面量以及調(diào)用構(gòu)造函數(shù) //對(duì)象字面量 va...
閱讀 2752·2021-10-22 09:55
閱讀 2157·2021-09-27 13:35
閱讀 1339·2021-08-24 10:02
閱讀 1641·2019-08-30 15:55
閱讀 1277·2019-08-30 14:13
閱讀 3539·2019-08-30 13:57
閱讀 2053·2019-08-30 11:07
閱讀 2537·2019-08-29 17:12