高能!typeof Function.prototype 引起的先有 Function 仍是先有 Object 的探討

來個摸底測試,說出如下每一個表達式的結果瀏覽器

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 的實例,這時便引出文章的題目

究竟是先有 Function 仍是先有 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

這時候問題昇華爲

Function.prototype 和 Object.prototype 的關係。

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 上的資源的權利。

到這裏全部對象之間的關係就已經清除了,相信已經有很多人已經暈乎了,不要緊 我準備了圖(看不太清晰是由於上傳後被壓縮了):

clipboard.png

固然這裏我仍是省略了部分細節 譬如對應類型的 prototype 屬性對象均有 constructor 屬性指向該類型,以及省略部分類型。

對着這張圖從新看一遍本文和文章開頭的摸底,相信你會有收穫。

那麼爲何使用 typeof 獲取 Object.prototype 會返回 object 呢。

咱們知道瀏覽器底層對 JS 的實現的是基於 C/C++
經過上圖,咱們能夠猜想

瀏覽器在初始化JS 環境時都發生了些什麼

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 有想法了嗎?

以上均爲我的查閱及實踐總結的觀點。

謝謝~

相關文章
相關標籤/搜索