成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

JavaScript原型鏈以及Object,F(xiàn)unction之間的關(guān)系

zacklee / 2541人閱讀

摘要:由于一般所有的原型鏈最終都會指向頂端的,所以它們都是的。好了現(xiàn)在了,成了所有對象原型鏈的。

JavaScript里任何東西都是對象,任何一個對象內(nèi)部都有另一個對象叫__proto__,即原型,它可以包含任何東西讓對象繼承。當然__proto__本身也是一個對象,它自己也有自己的__proto__,這樣一級一級向上,就構(gòu)成了一個__proto__鏈,即原型鏈。當然原型鏈不會無限向上,它有個終點,可以稱為原型鏈的頂端,或者root,它是一個特殊的對象,它的__proto__為null。

obj.__proto__.__proto__......__proto__ === null;

但是對象并不是憑空產(chǎn)生的,它一般是某一個class,或者確切說是構(gòu)造函數(shù)的實例。JavaScript和其它面向?qū)ο蟮恼Z言不太一樣,它沒有所謂的class,而是用函數(shù)來模擬class。定義了一個函數(shù),實際上就定義了一個class,函數(shù)本身就是class的constructor,例如:

function foo() {}
var a = new foo();

這里創(chuàng)建了一個對象a,是foo的一個實例。既然a是一個對象,它就有原型__proto__,那a的__proto__是怎么設(shè)定的?這里就引出了prototype,它是函數(shù)才有的一個屬性,也是一個對象,一個函數(shù)創(chuàng)建的實例的__proto__就指向這個函數(shù)的prototype。所以a的__proto__被設(shè)定為foo.prototype,即:

a.__proto__ === foo.prototype;

當然foo.prototype也是個對象,它自然也有__proto__,默認指向Object.prototype,即:

foo.prototype.__proto__ === Object.prototype;

而Object.prototype已經(jīng)沒有原型了,它的__proto__是null。這是JavaScript里一個很特殊的對象,它就是原型鏈的頂端。

以上就構(gòu)成了由對象a開始的原型鏈:

a.__proto__ === foo.prototype;
a.__proto__.__proto__ === Object.prototype;

JavaScript因此而推斷出:

a instanceof foo === true;
a instanceof Object === true;

注意,這就是JavaScript判斷一個對象是否instanceof某個函數(shù)的依據(jù),即對象a的原型鏈上有沒有一個__proto__是這個函數(shù)的prototype,如果有,那么a就是這個函數(shù)的instance。由于一般所有的原型鏈最終都會指向頂端的Object.prototype,所以它們都是Object的instance。

prototype和__proto__有什么作用

我們可以設(shè)定

foo.prototype = {
  x : 2,
  y : 3,
}

這里用字面量創(chuàng)建一個對象賦給了foo.prototype,這樣foo所創(chuàng)建出來的任何對象的__proto__就都指向了它,因此它們就可以訪問里面的field,或者說可以認為它們繼承了這些field,例如:

var a = new foo();
a.x === 2;
a.y === 3;

不只是foo.prototype,繼續(xù)往上foo.prototype的__proto__,以及原型鏈上所有的__proto__都會被這個對象繼承。一般的對象的很多常用方法如toString,都是從原型鏈頂端的Object.prototype里繼承來的。

函數(shù)也是對象

上面說了,JavaScript里任何東西都是對象,包括函數(shù),可以稱為函數(shù)對象。所以foo也是對象,那foo的原型__proto__是什么?它是誰的instance?

JavaScript里定義了一個特殊的函數(shù)叫Function,可以稱作是所有函數(shù)的爸爸,所有的函數(shù)都是它的實例,因此你可以認為,定義foo的時候發(fā)生了這樣的事情:

var foo = new Function(args, function_body);

于是我們有:

foo.__proto__ === Function.prototype;
foo instanceof Function === true;

注意這里的Function.prototype,這也是JavaScript里一個特殊的對象,Chrome的console里要是輸入Function.prototype,根本什么也打印不出來,什么native code,就是說它是內(nèi)部實現(xiàn)的。

這個原型鏈還沒到頂,F(xiàn)unction.prototype仍然有原型__proto__,指向Object.prototype,所以我們最終有:

foo.__proto__.__proto__ === Object.prototype;
foo instanceof Object === true;

現(xiàn)在有個問題來了,那Function自己呢?它其實也是個函數(shù),也是個對象,它的__proto__指向誰?答案是它自己的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,不過后者嚴格來說并不準確,F(xiàn)unction并不是它自己創(chuàng)造自己的,而應(yīng)該看作JavaScript里原生的一個函數(shù)對象,只不過它的__proto__指向了它自己的prototype而已。

Function和Object的關(guān)系

這是JavaScript比較奇葩的一個地方,也是不太讓人容易接受的一點。

我們知道一般任何對象都是Object的instance,因為原型鏈的頂端都指向Object.prototype。那么Object本身是什么?Object也是個函數(shù),而任何函數(shù)都是Function的實例對象,比如Array,String,當然Object也包括在內(nèi),它也是Function的實例,即:

Object.__proto__ === Function.prototype;
Object instanceof Function === true

同時,F(xiàn)unction是個對象,它的原型是Function.__proto__,指向Function.prototype,并且這個原型鏈向上繼續(xù)指向Object.prototype,即:

Function.__proto__.__proto__ === Object.prototype;
Function instanceof Object === true

這樣就有了一個JavaScript里經(jīng)常說到的蛋雞問題:

Object instanceof Function === true
Function instanceof Object === true

到底誰先誰后,誰主誰次?關(guān)于這一點網(wǎng)上已經(jīng)有很多解釋,這里首先陳述我的觀點,是先有Function,它是主;后有Object,是次。以下是我個人的理解,可能并不準確。要看待這個問題,我們可以從JavaScript創(chuàng)造世界開始想象:

我們知道Object.prototype是原型鏈的root。但首先,現(xiàn)在世界上還沒有Object,更沒有Object.prototype?,F(xiàn)在只有個特殊的對象,姑且稱它為root_prototype,里面定義了些基本的field和method比如toString之類的,以后我們要讓所有的原型鏈都最終指向它。注意它沒有原型,它的__proto__是null,這也是它和所有其它JavaScript對象的區(qū)別,使它與眾不同,能有資格成為原型鏈的root。

然后定義Function。先看Function的prototype,我們只要知道這是一個特殊的對象,它的原型__proto__指向剛才的root_prototype,就是說Function.prototype.__proto__ === root_prototype,這樣它就算連上了原型鏈的root。

上面已經(jīng)講過了,F(xiàn)unction也是個對象,也有__proto__,指向Function自己的prototype,所以說白了Function也是個奇葩,是JavaScript里規(guī)定的一個特殊的東西。而Function.prototype的原型__proto__繼續(xù)指向root_prototype,所以Function也連上了原型鏈root。

所有的函數(shù),什么Array之類的,包括Object也是函數(shù),都是繼承Function的,就是說,任意函數(shù)foo.__proto__ === Function.prototype,所以我們自然有Object instanceof Function。

然后再看Object,它本來就是個函數(shù)而已,和其它函數(shù)沒什么區(qū)別,都繼承了Function??墒乾F(xiàn)在最關(guān)鍵的一步是,強行設(shè)定讓Object.prototype = root_prototype,這樣Object.prototype就成了原型鏈的root!注意這里的邏輯,是先有了root_prototype,然后規(guī)定Object.prototype等于它,這一步是人為規(guī)定的,這就是Object的特殊之處。你要是規(guī)定bar.prototype也等于root_prototype,那bar.prototype也成了原型鏈的的頂端。所以JavaScript里__proto__這個東西其實是很隨意的,放在哪個函數(shù)的prototype里,哪個函數(shù)就成了你爹。

好了現(xiàn)在Object.prototype === root_prototype了,成了所有對象原型鏈的root。那么由第3步的結(jié)論,F(xiàn)unction也是對象,是連上了root_prototype的,而現(xiàn)在root_prototype給Object.prototype了,那Function自然就是instanceof Object。

總結(jié)一下:
- 首先沒雞沒蛋,先有一個特殊對象root_prototype,它是上帝。
- 接下來應(yīng)該是先有Function,并且定義它的prototype和__proto__,都連上了root_prototype。
- 然后才有了Object,它是Function的instance,繼承了Function。這時候Object仍然只是個普通的函數(shù)。
- 然后規(guī)定Object.prototype = root_prototype,這時候Object才開始顯得特殊,成為了原型鏈的頂端,否則它和其它函數(shù)根本沒什么區(qū)別。
- 于是所有的東西,包括Function,都成了Object的instance了。

這里要強調(diào)Object和其它函數(shù)的不同之處。Object之所以特殊,就是因為Object的prototype被設(shè)定為了root_prototype,僅此而已;而其它函數(shù)例如foo,它的prototype只是一個普通的對象,這個對象的__proto__默認情況下指向root_prototype。至于為什么這樣設(shè)定,為什么Object會特殊化,大概只是因為Object這個名字起得好,而foo,bar沒那么特殊。所以說白了Object函數(shù)只是一個盛放root_prototype的容器而已,從而使它晉升為一個特殊的函數(shù)。

另外值得注意的是,obj instanceof function 并不意味著obj就是這個function創(chuàng)建出來的,只不過是obj的原型鏈上有function.prototype而已。

所以所謂的 Object instanceof Function 和 Function instanceof Object 的蛋雞問題,前者應(yīng)該來說是自然而然、不容置疑的,可以認為Object函數(shù)是Function創(chuàng)造出來的;而后者說白了只是因為強行規(guī)定了Object函數(shù)的特殊性,而導(dǎo)致的一個推論,而Function并非是Object創(chuàng)建的。

當然這些概念繞來繞去討論其實我感覺沒什么很大意義,無非只是自圓其說而已。大家寫代碼時也不會太糾結(jié)這些問題,重點還是要把原型鏈搞清楚。

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/90361.html

相關(guān)文章

  • JavaScript 原型哲學(xué)思想

    摘要:而作為構(gòu)造函數(shù),需要有個屬性用來作為以該構(gòu)造函數(shù)創(chuàng)造的實例的繼承。 歡迎來我的博客閱讀:「JavaScript 原型中的哲學(xué)思想」 記得當年初試前端的時候,學(xué)習(xí)JavaScript過程中,原型問題一直讓我疑惑許久,那時候捧著那本著名的紅皮書,看到有關(guān)原型的講解時,總是心存疑慮。 當在JavaScript世界中走過不少旅程之后,再次萌發(fā)起研究這部分知識的欲望,翻閱了不少書籍和資料,才搞懂...

    sugarmo 評論0 收藏0
  • JavaScript進階 - 1. 原型原型概念

    摘要:對應(yīng)的關(guān)系圖如下講解了構(gòu)造函數(shù)和原型對象之間的關(guān)系,那么實例對象和原型對象之間的關(guān)系又是怎么樣的呢下面講解。原型對象的指向的是構(gòu)造函數(shù)和本身沒有屬性,但是其原型對象有該屬性,因此也能獲取到構(gòu)造函數(shù)。 JavaScript進階 - 1. 原型和原型鏈的概念 我們好多經(jīng)常會被問道JavaScript原型和原型鏈的概念,還有關(guān)于繼承,new操作符相關(guān)的概念。本文就專門整理了原型和原型鏈的概念...

    elisa.yang 評論0 收藏0
  • JS中原型原型深入理解

    摘要:如果要理清原型和原型鏈的關(guān)系,首先要明確一下幾個概念中的所有東西都是對象,函數(shù)也是對象而且是一種特殊的對象中所有的東西都由衍生而來即所有東西原型鏈的終點指向?qū)ο蠖加幸粋€隱藏的屬性,他指向創(chuàng)建它的構(gòu)造函數(shù)的原型,但是有一個例外,指向的是。 首先要搞明白幾個概念: 函數(shù)(function) 函數(shù)對象(function object) 本地對象(native object) 內(nèi)置對象(bu...

    Alex 評論0 收藏0
  • 探索 proto & prototype 與繼承之間關(guān)系

    摘要:而和的存在就是為了建立這種子類與父類間的聯(lián)系。創(chuàng)建一個基本對象建立新對象與原型我把它理解為類之間的連接執(zhí)行構(gòu)造函數(shù)小結(jié)可以理解為類,也就是存儲一類事物的基本信息。原型原型鏈和繼承之間的關(guān)系。 原型 原型的背景 首先,你應(yīng)該知道javascript是一門面向?qū)ο笳Z言。 是對象,就具有繼承性。 繼承性,就是子類自動共享父類的數(shù)據(jù)結(jié)構(gòu)和方法機制。 而prototype 和 __proto__...

    dockerclub 評論0 收藏0
  • 淺談javascript原型原型

    摘要:在中,主要有兩種創(chuàng)建對象的方法分別是對象字面量以及調(diào)用構(gòu)造函數(shù)對象字面量調(diào)用構(gòu)造函數(shù)其實上述兩種創(chuàng)建對象的方法,本質(zhì)上是一樣的,都是引擎調(diào)用對象的構(gòu)造函數(shù)來新建出一個對象。 原型與原型鏈是javascript里面最最核心的內(nèi)容,如果不能理解它們之間的存在關(guān)系的話,那么我們是不能理解這門語言的。 在JS中,主要有兩種創(chuàng)建對象的方法, 分別是對象字面量以及調(diào)用構(gòu)造函數(shù) //對象字面量 va...

    includecmath 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<