由一段代碼引起的關於Object和Function的雞和蛋問題的思考

做爲一名前端開發者,咱們都知道JS是單繼承的,而Object.prototype是原型鏈的頂端,全部對象從它繼承了包括toString()、valueOf()等等公共屬性。javascript

雞和蛋問題的由來

首先ObjectFunction都是構造函數,而全部的構造函數都是Function的實例對象。 所以ObjectFunction的實例對象;而Function.prototypeObject的實例對象。因此這裏就引申出了一個有意思的雞和蛋的問題:前端

Object instanceof Function  // true
Function instanceof Object  // true

Object.__proto__ === Function.prototype  // true
Function.__proto__ === Function.prototype  // true
Function.prototype.__proto__ === Object.prototype  // true
複製代碼

那麼ObjectFunction,誰是雞誰是蛋呢?java

接下來就來深刻探究下上面這段代碼所引發的雞生蛋蛋生雞問題,從下面這張原型/原型鏈經典圖入手,在這個過程當中深刻了解 Object.prototypeFunction.prototypefunction Object()function Function() 之間的關係,這個過程可能有點燒腦,畢竟是JS的一大玄學嘛。git

Object.prototype

原型鏈的盡頭就是Object.prototype(不考慮 null 的狀況下)。全部對象均從Object.prototype繼承toString() 等公共屬性github

Object.prototype 表示 Object 的原型對象,實際上Object.prototype 並非經過Object函數建立的,爲何呢?看以下代碼:瀏覽器

function Dog() {
  this.name = '川普';
}
var dog = new Dog();
dog.__proto__ === Dog.prototype;  // true
複製代碼

實例對象的__proto__會指向構造函數的prototype,即dog.__proto__指向 Dog.prototype,可是Object.prototype.__proto__又是 null,因此 Object.prototype 並非經過 Object 函數建立的,那它如何生成的?其實 Object.prototype 是瀏覽器底層根據 ECMAScript 規範創造的一個對象,因此在經典圖裏面只是看起來Object.prototype 是經過 Object 函數建立的,實際上並非。函數

Function.prototype

Function.prototypeFunction.__proto__同一對象ui

這也意味着:Object/Array等等構造函數本質上和Function同樣,均繼承於Function.prototype,從經典圖上來看都是經過new Function構造出來的this

固然,Function.prototype 對象是一個函數(對象),其__proto__屬性指向 Object.prototype,即Function.prototype會直接繼承root(Object.prototype)。spa

經過這點咱們能夠弄清繼承的原型鏈Function|Object|Array...--->Function.prototype--->Object.prototype(root)。以下圖所示:

function Object()

Object 做爲構造函數時,其__proto__屬性指向 Function.prototype,即:

Object.__proto__ === Function.prototype  // true
複製代碼

從經典圖來看:

使用 new Object() 建立實例對象o1時,實例對象o1的 __proto__屬性指向構造函數的 prototype 屬性,對應上圖就是 Object.prototype,即o1.__proto__ === Object.prototype結果爲true

Function.prototype指向的對象,它的__proto__會指向Object.prototype,由於Function.prototype指向的對象也是一個普通的被Object建立的對象,因此也遵循基本的規則。

function Function()

Function也是一個函數對象,也有__proto__屬性,既然是函數,那麼它必定是被Function建立,因此Function是被自身建立的,因此它的__proto__指向了自身的Prototype

Function.__proto__ === Function.prototype  // true
複製代碼

到這裏就有點燒腦了吧,咱們再看下雞生蛋蛋生雞問題。

Function & Object 雞和蛋問題

由上面可知,Object構造函數繼承了Function.prototype,同時Function構造函數繼承了Object.prototype,這裏就產生了雞和蛋的問題。爲何會出現這種問題呢?必須首先更深刻一層去理解Function.prototype這個對象,由於它是致使Function instanceof ObjectObject instanceof Function都爲true的緣由。

// Object instanceof Function 即
Object.__proto__ === Function.prototype   // true

// Function instanceof Object 即
Function.__proto__.__proto__ === Object.prototype   // true

// Object instanceof Object 即 
Object.__proto__.__proto__ === Object.prototype   // true

// Function instanceof Function 即 
Function.__proto__ === Function.prototype   // true
複製代碼

根據JS規範,Function.prototype又是個不一樣於通常函數(對象)的函數(對象),其中:

  1. Function.prototype像普通函數同樣能夠調用,但老是返回undefined
  2. 普通函數其實是Function的實例,即普通函數繼承於Function.prototype。即func.__proto__ === Function.prototype
  3. Function.prototype繼承於Object.prototype,而且沒有prototype這個屬性。
  4. 因此,Function.prototype實際上是個另類的函數,能夠獨立於/先於Function產生。

Object自己是個(構造)函數,是Function的實例,即Object.__proto__就是Function.prototype

總結:先有Object.prototype(原型鏈頂端),Function.prototype繼承Object.prototype而產生,最後,FunctionObject和其它構造函數繼承Function.prototype而產生

看到這裏估計也都看煩了,是否是仍是有點混亂呀?亂也很正常。那這篇文章就先讓它亂着,下一篇咱們將請另外一個老朋友來幫忙,把它完全理清楚,這位老朋友就是——instanceof,那就且聽請下回分解咯。

若是以爲文章對你有些許幫助,歡迎在個人GitHub博客點贊和關注,感激涕零!

相關文章
相關標籤/搜索