更新:謝謝你們的支持,最近折騰了一個博客官網出來,方便你們系統閱讀,後續會有更多內容和更多優化,猛戳這裏查看前端
------ 如下是正文 ------webpack
上篇文章用圖解的方式向你們介紹了原型鏈及其繼承方案,在介紹原型鏈繼承的過程當中講解原型鏈運做機制以及屬性遮蔽等知識,今天這篇文章就來深刻探究下 Function.__proto__ === Function.prototype
引發的雞生蛋蛋生雞問題,並在這個過程當中深刻了解 Object.prototype、Function.prototype、function Object 、function Function 之間的關係。git
咱們先來看看 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
咱們先來看看 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 thevalueOf
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…
將無休無止而且沒有存在的意義。
咱們先來看看 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。[[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
複製代碼
咱們先來看看 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
複製代碼
到這裏就有意思了,咱們看下雞生蛋蛋生雞問題。
咱們看下面這段代碼
Object instanceof Function // true
Function instanceof Object // true
Object instanceof Object // true
Function instanceof Function // true
複製代碼
Object
構造函數繼承了 Function.prototype
,同時 Function
構造函數繼承了Object.prototype
。這裏就產生了 雞和蛋 的問題。爲何會出現這種問題,由於 Function.prototype
和 Function.__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
上的方法。
最後給一個完整的圖,看懂這張圖原型就沒問題了。
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過程當中的一些收穫
進階系列文章彙總以下,以爲不錯點個Star,歡迎 加羣 互相學習。
我是木易楊,公衆號「高級前端進階」做者,跟着我每週重點攻克一個前端面試重難點。接下來讓我帶你走進高級前端的世界,在進階的路上,共勉!