【進階5-3期】深刻探究 Function & Object 雞蛋問題

更新:謝謝你們的支持,最近折騰了一個博客官網出來,方便你們系統閱讀,後續會有更多內容和更多優化,猛戳這裏查看前端

------ 如下是正文 ------webpack

引言

上篇文章用圖解的方式向你們介紹了原型鏈及其繼承方案,在介紹原型鏈繼承的過程當中講解原型鏈運做機制以及屬性遮蔽等知識,今天這篇文章就來深刻探究下 Function.__proto__ === Function.prototype 引發的雞生蛋蛋生雞問題,並在這個過程當中深刻了解 Object.prototype、Function.prototype、function Object 、function Function 之間的關係。git

Object.prototype

咱們先來看看 ECMAScript 上的定義(15.2.4)。github

The value of the [[Prototype]] internal property of the Object prototype object is null, the value of the [[Class]] internal property is "Object", and the initial value of the [[Extensible]] internal property is true.web

Object.prototype 表示 Object 的原型對象,其 [[Prototype]] 屬性是 null,訪問器屬性 __proto__ 暴露了一個對象的內部 [[Prototype]] 。 Object.prototype 並非經過 Object 函數建立的,爲何呢?看以下代碼面試

function Foo() {
  this.value = 'foo';
}
let f = new Foo();
f.__proto__ === Foo.prototype;
// true
複製代碼

實例對象的 __proto__ 指向構造函數的 prototype,即 f.__proto__ 指向 Foo.prototype,可是 Object.prototype.__proto__ 是 null,因此 Object.prototype 並非經過 Object 函數建立的,那它如何生成的?其實 Object.prototype 是瀏覽器底層根據 ECMAScript 規範創造的一個對象。算法

Object.prototype 就是原型鏈的頂端(不考慮 null 的狀況下),全部對象繼承了它的 toString 等方法和屬性。segmentfault

Function.prototype

咱們先來看看 ECMAScript 上的定義(15.3.4)。跨域

The Function prototype object is itself a Function object (its [[Class]] is "Function").數組

The value of the [[Prototype]] internal property of the Function prototype object is the standard built-in Object prototype object.

The Function prototype object does not have a valueOf property of its own; however, it inherits the valueOf property from the Object prototype Object.

Function.prototype 對象是一個函數(對象),其 [[Prototype]] 內部屬性值指向內建對象 Object.prototype。Function.prototype 對象自身沒有 valueOf 屬性,其從 Object.prototype 對象繼承了valueOf 屬性。

Function.prototype 的 [[Class]] 屬性是 Function,因此這是一個函數,但又不大同樣。爲何這麼說呢?由於咱們知道只有函數纔有 prototype 屬性,但並非全部函數都有這個屬性,由於 Function.prototype 這個函數就沒有。

Function.prototype
// ƒ () { [native code] }

Function.prototype.prototype
// undefined
複製代碼

固然你會發現下面這個函數也沒有 prototype 屬性。

let fun = Function.prototype.bind()
// ƒ () { [native code] }

fun.prototype
// undefined
複製代碼

爲何沒有呢,個人理解是 Function.prototype 是引擎建立出來的函數,引擎認爲不須要給這個函數對象添加 prototype 屬性,否則 Function.prototype.prototype… 將無休無止而且沒有存在的意義。

function Object

咱們先來看看 ECMAScript 上的定義(15.2.3)。

The value of the [[Prototype]] internal property of the Object constructor is the standard built-in Function prototype object.

Object 做爲構造函數時,其 [[Prototype]] 內部屬性值指向 Function.prototype,即

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

使用 new Object() 建立新對象時,這個新對象的 [[Prototype]] 內部屬性指向構造函數的 prototype 屬性,對應上圖就是 Object.prototype。

固然也能夠經過對象字面量等方式建立對象。

  • 使用對象字面量建立的對象,其 [[Prototype]] 值是 Object.prototype
  • 使用數組字面量建立的對象,其 [[Prototype]] 值是 Array.prototype
  • 使用 function f(){} 函數建立的對象,其 [[Prototype]] 值是 Function.prototype
  • 使用 new fun() 建立的對象,其中 fun 是由 JavaScript 提供的內建構造器函數之一(Object, Function, Array, Boolean, Date, Number, String 等等),其 [[Prototype]] 值是 fun.prototype。
  • 使用其餘 JavaScript 構造器函數建立的對象,其 [[Prototype]] 值就是該構造器函數的 prototype 屬性。
let o = {a: 1};
// 原型鏈: o ---> Object.prototype ---> null

let a = ["yo", "whadup", "?"];
// 原型鏈: a ---> Array.prototype ---> Object.prototype ---> null

function f(){
  return 2;
}
// 原型鏈: f ---> Function.prototype ---> Object.prototype ---> null

let fun = new Function();
// 原型鏈: fun ---> Function.prototype ---> Object.prototype ---> null

function Foo() {}
let foo = new Foo();
// 原型鏈: foo ---> Foo.prototype ---> Object.prototype ---> null

function Foo() {
  return {};
}
let foo = new Foo();
// 原型鏈: foo ---> Object.prototype ---> null
複製代碼

function Function

咱們先來看看 ECMAScript 上的定義(15.3.3)。

The Function constructor is itself a Function object and its [[Class]] is "Function". The value of the [[Prototype]] internal property of the Function constructor is the standard built-in Function prototype object.

Function 構造函數是一個函數對象,其 [[Class]] 屬性是 Function。Function 的 [[Prototype]] 屬性指向了 Function.prototype,即

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

到這裏就有意思了,咱們看下雞生蛋蛋生雞問題。

Function & Object 雞蛋問題

咱們看下面這段代碼

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

Object instanceof Object 			// true
Function instanceof Function 	// true
複製代碼

Object 構造函數繼承了 Function.prototype,同時 Function 構造函數繼承了Object.prototype。這裏就產生了 雞和蛋 的問題。爲何會出現這種問題,由於 Function.prototypeFunction.__proto__ 都指向 Function.prototype

// 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
複製代碼

對於 Function.__proto__ === Function.prototype 這一現象有 2 種解釋,爭論點在於 Function 對象是否是由 Function 構造函數建立的一個實例?

解釋 一、YES:按照 JavaScript 中「實例」的定義,a 是 b 的實例即 a instanceof b 爲 true,默認判斷條件就是 b.prototype 在 a 的原型鏈上。而 Function instanceof Function 爲 true,本質上即 Object.getPrototypeOf(Function) === Function.prototype,正符合此定義。

解釋 二、NO:Function 是 built-in 的對象,也就是並不存在「Function對象由Function構造函數建立」這樣顯然會形成雞生蛋蛋生雞的問題。實際上,當你直接寫一個函數時(如 function f() {}x => x),也不存在調用 Function 構造器,只有在顯式調用 Function 構造器時(如 new Function('x', 'return x') )纔有。

我我的偏向於第二種解釋,即先有 Function.prototype 而後有的 function Function() ,因此就不存在雞生蛋蛋生雞問題了,把 Function.__proto__ 指向 Function.prototype 是爲了保證原型鏈的完整,讓 Function 能夠獲取定義在 Object.prototype 上的方法。

最後給一個完整的圖,看懂這張圖原型就沒問題了。

jsobj

內置類型構建過程

JavaScript 內置類型是瀏覽器內核自帶的,瀏覽器底層對 JavaScript 的實現基於 C/C++,那麼瀏覽器在初始化 JavaScript 環境時都發生了什麼?

沒找到官方文檔,下文參考自 segmentfault.com/a/119000000…

一、用 C/C++ 構造內部數據結構建立一個 OP 即 (Object.prototype) 以及初始化其內部屬性但不包括行爲。

二、用 C/C++ 構造內部數據結構建立一個 FP 即 (Function.prototype) 以及初始化其內部屬性但不包括行爲。

三、將 FP 的 [[Prototype]] 指向 OP。

四、用 C/C++ 構造內部數據結構建立各類內置引用類型。

五、將各內置引用類型的[[Prototype]]指向 FP。

六、將 Function 的 prototype 指向 FP。

七、將 Object 的 prototype 指向 OP。

八、用 Function 實例化出 OP,FP,以及 Object 的行爲並掛載。

九、用 Object 實例化出除 Object 以及 Function 的其餘內置引用類型的 prototype 屬性對象。

十、用 Function 實例化出除Object 以及 Function 的其餘內置引用類型的 prototype 屬性對象的行爲並掛載。

十一、實例化內置對象 Math 以及 Grobal

至此,全部內置類型構建完成。

參考

從探究Function.__proto__===Function.prototype過程當中的一些收穫

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

從__proto__和prototype來深刻理解JS對象和原型鏈

文章穿梭機

進階系列目錄

  • 【進階1期】 調用堆棧
  • 【進階2期】 做用域閉包
  • 【進階3期】 this全面解析
  • 【進階4期】 深淺拷貝原理
  • 【進階5期】 原型Prototype
  • 【進階6期】 高階函數
  • 【進階7期】 事件機制
  • 【進階8期】 Event Loop原理
  • 【進階9期】 Promise原理
  • 【進階10期】Async/Await原理
  • 【進階11期】防抖/節流原理
  • 【進階12期】模塊化詳解
  • 【進階13期】ES6重難點
  • 【進階14期】計算機網絡概述
  • 【進階15期】瀏覽器渲染原理
  • 【進階16期】webpack配置
  • 【進階17期】webpack原理
  • 【進階18期】前端監控
  • 【進階19期】跨域和安全
  • 【進階20期】性能優化
  • 【進階21期】VirtualDom原理
  • 【進階22期】Diff算法
  • 【進階23期】MVVM雙向綁定
  • 【進階24期】Vuex原理
  • 【進階25期】Redux原理
  • 【進階26期】路由原理
  • 【進階27期】VueRouter源碼解析
  • 【進階28期】ReactRouter源碼解析

交流

進階系列文章彙總以下,以爲不錯點個Star,歡迎 加羣 互相學習。

github.com/yygmind/blo…

我是木易楊,公衆號「高級前端進階」做者,跟着我每週重點攻克一個前端面試重難點。接下來讓我帶你走進高級前端的世界,在進階的路上,共勉!

相關文章
相關標籤/搜索