來個摸底測試,說出如下每一個表達式的結果瀏覽器
function F(){}; var o = {}; typeof F; typeof o; typeof F.prototype; typeof o.prototype; typeof new F; typeof (new F).prototype; typeof (new F).__proto__; typeof F.__proto__; typeof o.__proto__; typeof Object; typeof Function; typeof (new Function).prototype; typeof (new Function).__proto__; typeof (new Object).prototype; typeof (new Object).__proto__; typeof Object.prototype; typeof Object.__proto__; typeof Function.prototype; typeof Function.__proto__;
function F(){}; var o = {}; typeof F; //==> function typeof o; //==> object typeof F.prototype; //==> object typeof o.prototype; //==> undefinded typeof new F; //==> object typeof (new F).prototype; //==> undefined typeof (new F).__proto__; //==> object typeof F.__proto__; //==> function typeof o.__proto__; //==> object typeof Object; //==> function typeof Function; //==> function typeof (new Function).prototype; //==> object typeof (new Function).__proto__; //==> function typeof (new Object).prototype; //==> undefined typeof (new Object).__proto__; //==> object typeof Object.prototype; //==> object typeof Object.__proto__; //==> function typeof Function.prototype; //==> function typeof Function.__proto__; //==> function
看到這裏相信有很多入門不久的同窗已經產生疑惑了 是真的嗎 而後在瀏覽器試過一番發現真是如此。數據結構
解開疑惑以前先回顧些你們都知道的知識點:函數
引用 MDN 關於 對象實例和對象原型對象 的闡述:學習
JavaScript語言的全部對象都是由Object衍生的對象;
全部對象都繼承了Object.prototype的方法和屬性,儘管它們可能被覆蓋。
例如,其它的構造器原型覆蓋了constructor屬性並提供了其本身的toString方法。
原型對象的更改會傳播給全部的對象,除非這些屬性和方法在原型鏈中被再次覆蓋。測試
就如咱們常常在各種教科中看到的 全部的實例對象都是 Object 類型的實例this
那麼咱們平時都是如何肯定一個對象是不是另外一個類型或對象的實例的呢?spa
對咱們可使用 typeof 關鍵字 亦或可使用關鍵字 instanceof 來肯定某個對象是不是指定類型或對象的實例:prototype
typeof {} //object ({}) instanceof Object //true typeof Date //function Date instanceof Function //true typeof Date.prototype //obejct Date.prototype instanceof Object //true
然而針對 Object 的 prototype 屬性:code
typeof Object.prototype //object Object.prototype instanceof Object // false
爲何,要想搞清楚爲何就得明白 instanceof 這個關鍵字在表達式中發生了什麼?對象
在弄清楚 instanceof 以前 還得弄清楚同樣東西 就是 new 一個對象到底作了什麼:
如 var a = new A(); 認爲 「a爲A函數的實例對象」
new操做的過程是什麼? 能夠總結以下:
1.new 建立一個空對象{}
2.而後將A.prototype的引用放置到該對象的原型鏈上。即a.__proto__指向 A.prototype
3.執行A函數,將A中this指向該對象,執行結束,若是沒有return那麼默認返回this引用
那麼new的其中一個的做用即是把A.prototype的指向添加到了a的原型鏈中。
至此咱們便知道了以下關係:
a.__proto__ === A.prototype //true a instanceof A //true
故爲什麼不得出一個結論:
instanceof 操做符其實就是檢查左側的元素的 __proto__鏈上有沒有右側類或對象的prototype存在。 同理 當某某某是某某某的實例時 其實也是證實左側的__proto__鏈上有右側類或對象的prototype存在。
細節剖析以下:
1.看右側的 A 獲取其 prototype 獲得 A.prototype。
2.看左側 a 對象的原型鏈上是否有第一步獲得 A.prototype。
1)獲取 a.__proto__對象看是否爲A.prototype,是則返回 true
2)獲取 a.__proto__.__proto__ 對象看是否爲A.prototype,是則返回 true
3)重複獲取左側的原型鏈上的[[Prototype]]
特性即__proto__屬性進行判斷直到爲空返回 false。
校驗真理,咱們都知道 js 有幾大內置類型 這些類型都是 Function 的實例,是 Function 類型:
out(typeof Date) //Function out(typeof RegExp) //Function out(typeof Number) //Function out(typeof Boolean) //Function out(typeof String) //Function out(typeof Array) //Function out(typeof Error) //Function //... out(Date.__proto__ === Function.prototype) //true out(RegExp.__proto__ === Function.prototype) //true out(Number.__proto__ === Function.prototype) //true out(Boolean.__proto__ === Function.prototype) //true out(String.__proto__ === Function.prototype) //true out(Array.__proto__ === Function.prototype) //true out(Error.__proto__ === Function.prototype) //true //... out(Object.getPrototypeOf(Date) === Function.prototype) //true out(Object.getPrototypeOf(RegExp) === Function.prototype) //true out(Object.getPrototypeOf(Number) === Function.prototype) //true out(Object.getPrototypeOf(Boolean) === Function.prototype) //true out(Object.getPrototypeOf(String) === Function.prototype) //true out(Object.getPrototypeOf(Array) === Function.prototype) //true out(Object.getPrototypeOf(Error) === Function.prototype) //true //... out( Date instanceof Function) //true out( RegExp instanceof Function) //true out( Number instanceof Function) //true out( Boolean instanceof Function) //true out( String instanceof Function) //true out( Array instanceof Function) //true out( Error instanceof Function) //true //...
回到上述針對 Object 的 prototype 屬性疑惑 爲何到了 Object 就得不出同樣的結果了呢?
咱們都知道每一個函數對象亦或函數類型都會有個 prototype 屬性,在其上掛載的方法和屬性均可以被該類型實例化出來的對象共享,由於實例化出來的對象擁有 [[Prototype]]
特性即 __proto__
屬性,js 即是經過該特性實現原型鏈機制。
那麼這些函數的 prototype 屬性對象是否也有本身的[[Prototype]]
特性即 __proto__
屬性呢?
out(typeof Date.prototype) //object out(typeof RegExp.prototype) //object out(typeof Number.prototype) //object out(typeof Boolean.prototype) //object out(typeof String.prototype) //object out(typeof Array.prototype) //object out(typeof Error.prototype) //object out(typeof Object.getPrototypeOf(Date.prototype)) //object out(typeof Object.getPrototypeOf(RegExp.prototype)) //object out(typeof Object.getPrototypeOf(Number.prototype)) //object out(typeof Object.getPrototypeOf(Boolean.prototype)) //object out(typeof Object.getPrototypeOf(String.prototype)) //object out(typeof Object.getPrototypeOf(Array.prototype)) //object out(typeof Object.getPrototypeOf(Error.prototype)) //object out(Object.getPrototypeOf(Date.prototype) === Object.prototype) //true out(Object.getPrototypeOf(RegExp.prototype) === Object.prototype) //true out(Object.getPrototypeOf(Number.prototype) === Object.prototype) //true out(Object.getPrototypeOf(Boolean.prototype) === Object.prototype) //true out(Object.getPrototypeOf(String.prototype) === Object.prototype) //true out(Object.getPrototypeOf(Array.prototype) === Object.prototype) //true out(Object.getPrototypeOf(Error.prototype) === Object.prototype) //true
能夠看出每一個函數對象的 prototype 屬性也有本身的[[Prototype]]
特性 並且指向的是 Object.prototype
那麼是否全部對象都會有直接的[[Prototype]]
特性 呢?
out( Object.getPrototypeOf( Object.getPrototypeOf(Date.prototype))) //null out( Object.getPrototypeOf( Object.getPrototypeOf(RegExp.prototype))) //null out( Object.getPrototypeOf( Object.getPrototypeOf(Number.prototype))) //null out( Object.getPrototypeOf( Object.getPrototypeOf(Boolean.prototype))) //null out( Object.getPrototypeOf( Object.getPrototypeOf(String.prototype))) //null out( Object.getPrototypeOf( Object.getPrototypeOf(Array.prototype))) //null out( Object.getPrototypeOf( Object.getPrototypeOf(Error.prototype))) //null out( Object.getPrototypeOf( Object.prototype)) //null
答案是否。
有個例外他就是 Object.prototype
。
回看前面的 Demo:
Object.prototype instanceof Object // false
從前面的代碼輸出咱們看到 Object.prototype 對象是沒有[[Prototype]]
特性的,同時前面咱們也討論過 instanceof
這個關鍵字所涉及的具體操做。
咱們能夠具體剖析以下:
1.看右側的 Object 獲取其 prototype 獲得 Object.prototype。
2.看左側 Object.prototype 對象的原型鏈上是否有第一步獲得 Object.prototype。
1)獲取 Object.prototype.__proto__對象爲空直接返回 false。
那麼爲何全部的對象都有[[Prototype]]
特性,惟獨Object.prototype
沒有呢。答案很簡單:既然 JS的繼承機制是基於原型鏈的那總該有個頭吧,這個頭即是Object.prototype
。
再來一發:
out( typeof Function) //function out( typeof Object) //function out( Object instanceof Function) //true out( Function instanceof Function) //true
在學習 JS 的過程當中咱們已經知道全部的數據類型都是 Function
類型的實例,包括 Object
在內,可是咱們都知道全部的對象都是 Object 的實例,這時便引出文章的題目
out( Function.__proto__ === Function.prototype) //true out( Object.__proto__ === Function.prototype) //true out( Object.getPrototypeOf(Function) === Function.prototype) //true out( Object.getPrototypeOf(Object) === Function.prototype) //true out( Object.getPrototypeOf(Function) === Function.prototype) //true out( Object.getPrototypeOf(Object) === Function.prototype) //true out( Object instanceof Function) //true out( Function instanceof Function) //true
從上述代碼加上前面得出的結論咱們能夠看出
Function 和 Object 的原型鏈上都有 Function.prototype
咱們再來詳細看看 Function.prototype
out( typeof Function.prototype); // function out( Function.prototype instanceof Object) //true
這時候問題昇華爲
out( Function.prototype.__proto__ === Object.prototype)
這時候關係已經很明瞭了:
咱們都知道除了 Object.prototype 以外全部對象都會有[[Prototype]]
特性 即 __proto__
屬性,故 Function.prototype 也不例外,
Function.prototype 指向的是 Object.prototype
這時候就能夠清楚的知道爲何說全部類型都是 Function 的實例,同時也是 Object 的實例:
由於全部的類型的
[[Prototype]]
特性 即__proto__
屬性均指向的是 Function.prototype ,同時 Function.prototype 的[[Prototype]]
特性 即__proto__
屬性又指向了 Object.prototype 。
故大王是Object.prototype
,二王是Function.prototype
,其餘均是小弟,可是小弟也有等級劃分
先接着來看 Function:
out( typeof Function.prototype); // function out( typeof Function.__proto__); // function out( Function.prototype === Function.__proto__);// true
首先咱們能夠看出 Function.prototype 和其餘類型的 prototype 屬性是 object 類型不同, 是 function 類型
其次 Function.__proto__ 指向了 Function.prototype。
咱們知道當一個類型實例化出對象時,這些對象的便會共享掛載在該類型的 prototype 屬性上的 資源,由於這些對象均有[[Prototype]]
特性,指向的就是實例化出這些對象的類型的 prototype
從前面的例子能夠看到全部的類型的[[Prototype]]
特性均指向了 Function.prototype,因此這些類型都具備了使用掛載在Function.prototype上的資源的權利。所以能夠看出,當一個對象具備使用掛載在Function.prototype上的資源的權利時,及該對象[[Prototype]]
指向 Function.prototype 時表明這個對象是個 Function 實例是一個類型,可以實例化出該類型的對象,固然包括 Function 在內。
又由於全部類型的[[Prototype]]
指向 Function.prototype 而 Function.prototype 的[[Prototype]]
指向是 Object.prototype,因此這些類型都具備使用掛載在 Object.prototype 上的資源的權利。
那用這些類型實例化出來的對象呢 類型的原型鏈並不在實例化出來的對象上呀,可是這些實例化出來的對象的[[Protitype]]
指向的是其類型的 prototype 屬性
往回看前面的例子 能夠看到有一段
out(Object.getPrototypeOf(Date.prototype) === Object.prototype) //true out(Object.getPrototypeOf(RegExp.prototype) === Object.prototype) //true out(Object.getPrototypeOf(Number.prototype) === Object.prototype) //true out(Object.getPrototypeOf(Boolean.prototype) === Object.prototype) //true out(Object.getPrototypeOf(String.prototype) === Object.prototype) //true out(Object.getPrototypeOf(Array.prototype) === Object.prototype) //true out(Object.getPrototypeOf(Error.prototype) === Object.prototype) //true out(Object.getPrototypeOf(Function.prototype) === Object.prototype) //true
能夠看到這些類型的 prototype 屬性的[[Protitype]]
指向的是 Object.prototype 故至此,全部對象包括類型對象亦或類型實例化出來的對象都有使用掛載在 Object.prototype 上的資源的權利。
到這裏全部對象之間的關係就已經清除了,相信已經有很多人已經暈乎了,不要緊 我準備了圖(看不太清晰是由於上傳後被壓縮了):
固然這裏我仍是省略了部分細節 譬如對應類型的 prototype 屬性對象均有 constructor 屬性指向該類型,以及省略部分類型。
對着這張圖從新看一遍本文和文章開頭的摸底,相信你會有收穫。
那麼爲何使用 typeof 獲取 Object.prototype 會返回 object 呢。
咱們知道瀏覽器底層對 JS 的實現的是基於 C/C++
經過上圖,咱們能夠猜想
1.用 C/C++ 構造內部數據結構建立一個 OP 即(Object.prototype)以及初始化其內部屬性但不包括行爲。
2.用 C/C++ 構造內部數據結構建立一個 FP 即(Function.prototype)以及初始化其內部屬性但不包括行爲。
3.將 FP 的[[Prototype]]指向 OP。
4.用 C/C++ 構造內部數據結構建立各類內置引用類型。
5.將各內置引用類型的[[Prototype]]指向 FP。
6.將 Function 的 prototype 指向 FP。
7.將 Object 的 prototype 指向 OP。
8.用 Function 實例化出 OP,FP,以及 Object 的行爲並掛載。
9.用 Object 實例化出除 Object 以及 Function 的其餘內置引用類型的 prototype 屬性對象。
10.用 Function 實例化出除Object 以及 Function 的其餘內置引用類型的 prototype 屬性對象的行爲並掛載。
11.實例化內置對象 Math 以及 Grobal
至此,全部 內置類型構建完成。
如今咱們能夠回答爲何使用 typeof 獲取 Object.prototype 會返回 object 了。
由於咱們在使用 typeof 的時候獲得的 object 類型並不徹底表明是 Object 類型實例化出來的對象,有多是底層實現的內部數據結構,包括 null。真正想知道這個類型仍是須要去到當前該類的內部[[Class]]
屬性,至於如何獲取可使用Object的toString方法。
最後的最後,你還對是現有 Function 仍是現有 Object 有想法了嗎?
以上均爲我的查閱及實踐總結的觀點。
謝謝~