JavaScript原型鏈以及Object,Function之間的關係

JavaScript裏任何東西都是對象,任何一個對象內部都有另外一個對象叫__proto__,即原型,它能夠包含任何東西讓對象繼承。固然__proto__自己也是一個對象,它本身也有本身的__proto__,這樣一級一級向上,就構成了一個__proto__鏈,即原型鏈。固然原型鏈不會無限向上,它有個終點,能夠稱爲原型鏈的頂端,或者root,它是一個特殊的對象,它的__proto__爲null。javascript

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

可是對象並非憑空產生的,它通常是某一個class,或者確切說是構造函數的實例。JavaScript和其它面向對象的語言不太同樣,它沒有所謂的class,而是用函數來模擬class。定義了一個函數,實際上就定義了一個class,函數自己就是class的constructor,例如:java

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

這裏建立了一個對象a,是foo的一個實例。既然a是一個對象,它就有原型__proto__,那a的__proto__是怎麼設定的?這裏就引出了prototype,它是函數纔有的一個屬性,也是一個對象,一個函數建立的實例的__proto__就指向這個函數的prototype。因此a的__proto__被設定爲foo.prototype,即:函數

a.__proto__ === foo.prototype;

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

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

而Object.prototype已經沒有原型了,它的__proto__是null。這是JavaScript裏一個很特殊的對象,它就是原型鏈的頂端code

以上就構成了由對象a開始的原型鏈:對象

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

JavaScript所以而推斷出:繼承

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

注意,這就是JavaScript判斷一個對象是否instanceof某個函數的依據,即對象a的原型鏈上有沒有一個__proto__是這個函數的prototype,若是有,那麼a就是這個函數的instance。因爲通常全部的原型鏈最終都會指向頂端的Object.prototype,因此它們都是Object的instance。ip

prototype和__proto__有什麼做用

咱們能夠設定原型鏈

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

這裏用字面量建立一個對象賦給了foo.prototype,這樣foo所建立出來的任何對象的__proto__就都指向了它,所以它們就能夠訪問裏面的field,或者說能夠認爲它們繼承了這些field,例如:原型

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

不僅是foo.prototype,繼續往上foo.prototype的__proto__,以及原型鏈上全部的__proto__都會被這個對象繼承。通常的對象的不少經常使用方法如toString,都是從原型鏈頂端的Object.prototype裏繼承來的。

函數也是對象

上面說了,JavaScript裏任何東西都是對象,包括函數,能夠稱爲函數對象。因此foo也是對象,那foo的原型__proto__是什麼?它是誰的instance?

JavaScript裏定義了一個特殊的函數叫Function,能夠稱做是全部函數的爸爸,全部的函數都是它的實例,所以你能夠認爲,定義foo的時候發生了這樣的事情:

var foo = new Function(args, function_body);

因而咱們有:

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

注意這裏的Function.prototype,這也是JavaScript裏一個特殊的對象,Chrome的console裏要是輸入Function.prototype,根本什麼也打印不出來,什麼native code,就是說它是內部實現的。

這個原型鏈還沒到頂,Function.prototype仍然有原型__proto__,指向Object.prototype,因此咱們最終有:

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

如今有個問題來了,那Function本身呢?它其實也是個函數,也是個對象,它的__proto__指向誰?答案是它本身的prototype,即:

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

總結一下:全部的函數都是Function的instance,Function本身也是它本身的instance,不事後者嚴格來講並不許確,Function並非它本身創造本身的,而應該看做JavaScript裏原生的一個函數對象,只不過它的__proto__指向了它本身的prototype而已。

Function和Object的關係

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

咱們知道通常任何對象都是Object的instance,由於原型鏈的頂端都指向Object.prototype。那麼Object自己是什麼?Object也是個函數,而任何函數都是Function的實例對象,好比Array,String,固然Object也包括在內,它也是Function的實例,即:

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

同時,Function是個對象,它的原型是Function.__proto__,指向Function.prototype,而且這個原型鏈向上繼續指向Object.prototype,即:

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

這樣就有了一個JavaScript裏常常說到的蛋雞問題:

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

到底誰先誰後,誰主誰次?關於這一點網上已經有不少解釋,這裏首先陳述個人觀點,是先有Function,它是主;後有Object,是次。如下是我我的的理解,可能並不許確。要看待這個問題,咱們能夠從JavaScript創造世界開始想象:

  1. 咱們知道Object.prototype是原型鏈的root。但首先,如今世界上尚未Object,更沒有Object.prototype。如今只有個特殊的對象,姑且稱它爲root_prototype,裏面定義了些基本的field和method好比toString之類的,之後咱們要讓全部的原型鏈都最終指向它。注意它沒有原型,它的__proto__是null,這也是它和全部其它JavaScript對象的區別,使它不同凡響,能有資格成爲原型鏈的root。
  2. 而後定義Function。先看Function的prototype,咱們只要知道這是一個特殊的對象,它的原型__proto__指向剛纔的root_prototype,就是說Function.prototype.__proto__ === root_prototype,這樣它就算連上了原型鏈的root。
  3. 上面已經講過了,Function也是個對象,也有__proto__,指向Function本身的prototype,因此說白了Function也是個奇葩,是JavaScript裏規定的一個特殊的東西。而Function.prototype的原型__proto__繼續指向root_prototype,因此Function也連上了原型鏈root。
  4. 全部的函數,什麼Array之類的,包括Object也是函數,都是繼承Function的,就是說,任意函數foo.__proto__ === Function.prototype,因此咱們天然有Object instanceof Function。
  5. 而後再看Object,它原本就是個函數而已,和其它函數沒什麼區別,都繼承了Function。但是如今最關鍵的一步是,強行設定讓Object.prototype = root_prototype,這樣Object.prototype就成了原型鏈的root!注意這裏的邏輯,是先有了root_prototype,而後規定Object.prototype等於它,這一步是人爲規定的,這就是Object的特殊之處。你要是規定bar.prototype也等於root_prototype,那bar.prototype也成了原型鏈的的頂端。因此JavaScript裏__proto__這個東西實際上是很隨意的,放在哪一個函數的prototype裏,哪一個函數就成了你爹。
  6. 好了如今Object.prototype === root_prototype了,成了全部對象原型鏈的root。那麼由第3步的結論,Function也是對象,是連上了root_prototype的,而如今root_prototype給Object.prototype了,那Function天然就是instanceof Object。
總結一下:
- 首先沒雞沒蛋,先有一個特殊對象root_prototype,它是上帝。
- 接下來應該是先有Function,而且定義它的prototype和__proto__,都連上了root_prototype。
- 而後纔有了Object,它是Function的instance,繼承了Function。這時候Object仍然只是個普通的函數。
- 而後規定Object.prototype = root_prototype,這時候Object纔開始顯得特殊,成爲了原型鏈的頂端,不然它和其它函數根本沒什麼區別。
- 因而全部的東西,包括Function,都成了Object的instance了。

這裏要強調Object和其它函數的不一樣之處。Object之因此特殊,就是由於Object的prototype被設定爲了root_prototype,僅此而已;而其它函數例如foo,它的prototype只是一個普通的對象,這個對象的__proto__默認狀況下指向root_prototype。至於爲何這樣設定,爲何Object會特殊化,大概只是由於Object這個名字起得好,而foo,bar沒那麼特殊。因此說白了Object函數只是一個盛放root_prototype的容器而已,從而使它晉升爲一個特殊的函數。

另外值得注意的是,obj instanceof function 並不意味着obj就是這個function建立出來的,只不過是obj的原型鏈上有function.prototype而已。

因此所謂的 Object instanceof Function 和 Function instanceof Object 的蛋雞問題,前者應該來講是天然而然、無可置疑的,能夠認爲Object函數是Function創造出來的;然後者說白了只是由於強行規定了Object函數的特殊性,而致使的一個推論,而Function並不是是Object建立的。

固然這些概念繞來繞去討論其實我感受沒什麼很大意義,無非只是自圓其說而已。你們寫代碼時也不會太糾結這些問題,重點仍是要把原型鏈搞清楚。

相關文章
相關標籤/搜索