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
咱們能夠設定原型鏈
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而已。
這是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創造世界開始想象:
- 首先沒雞沒蛋,先有一個特殊對象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建立的。
固然這些概念繞來繞去討論其實我感受沒什麼很大意義,無非只是自圓其說而已。你們寫代碼時也不會太糾結這些問題,重點仍是要把原型鏈搞清楚。