JavaScript原型系列(三)Function、Object、null等等的關係和雞蛋問題

將欲廢之,必固興之;將欲奪之,必固與之。——《道德經》javascript

JavaScript原型系列(一)構造函數、原型和原型鏈java

JavaScript原型系列(二)什麼是原型繼承git

JavaScript原型系列(三)Function、Object、Null等等的關係和雞蛋問題github

簡介

JavaScript-prototype

基本上都知道原型鏈的盡頭指向null,那麼Function.prototypeObject.prototypenullFunction.prototype.__proto__Object.prototype.__proto__function、object之間的關係是什麼,下面慢慢來記錄一下。c#

Object


Object 構造函數建立一個對象包裝器。JavaScript中的全部對象都來自 Object;全部對象從Object.prototype繼承方法和屬性,儘管它們可能被覆蓋。瀏覽器

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

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

JavaScript-prototype

Object.prototype

Object.prototype 表示 Object 的原型對象,其 [[Prototype]] 屬性是 null,訪問器屬性 __proto__ 暴露了一個對象的內部 [[Prototype]]Object.prototype是瀏覽器底層根據 ECMAScript 規範創造的一個對象。post

object

經過字面量實例化一個object,它的__proto__指向Object.prototypeui

var obj = {};
    obj.__proto__ === Object.prototype; // true
複製代碼

而經過new Object實例化一個object,它的__proto__指向Object.prototypespa

var obj = new Object;
    obj.__proto__ === Object.prototype; // true
複製代碼

Function


摘錄來自ECMAScript 5.1規範

對象類型的成員,標準內置構造器 Function的一個實例,而且可作爲子程序被調用。 注: 函數除了擁有命名的屬性,還包含可執行代碼、狀態,用來肯定被調用時的行爲。函數的代碼不限於 ECMAScript。

Function構造函數建立一個新的Function對象。在JavaScript中每一個函數實際上都是一個Function對象。

Function.prototype

全局的Function對象沒有本身的屬性和方法, 可是由於它自己也是函數,因此它也會經過原型鏈從Function.prototype上繼承部分屬性和方法。Function.prototype也是一個「函數對象「,其[[prototype]]內部屬性值指向Object.prototype

Function.prototype 的 [[Class]] 屬性是 Function,因此這是一個函數,但又不大同樣。

Function.prototype; // ƒ () { [native code] }
    Function.prototype.prototype; // undefined
複製代碼

用 Function.prototype.bind 建立的函數對象沒有 prototype 屬性。

let foo = Function.prototype.bind();
    foo.prototype; // undefined
複製代碼

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

Function.prototype.__proto__指向Object.prototype

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

Function.proto

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

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

JavaScript-prototype

function

實例化一個Function,它的__proto__指向Function.prototype

function foo () {}
    foo.__proto__ === Function.prototype; // true
    // foo.__proto__ => Function.prototype => Function.prototype.__proto__ => Object.prototype => Object.prototype.__proto__ => null
複製代碼

Object和Function的雞和蛋的問題


通過上面對ObjectFunction的闡述,延伸出來幾個問題以下:

  • 在忽濾null在原型鏈上時,原型鏈的盡頭(root)是Object.prototype。全部對象均從Object.prototype繼承屬性。

    JavaScript-prototype

  • Function.prototypeFunction.__proto__爲同一對象。

    JavaScript-prototype
    這意味着: Object/Array/String等等構造函數本質上和Function同樣,均繼承於Function.prototype

  • Function.prototype直接繼承root(Object.prototype)。

// Function.prototype繼承了Object.prototype
    Function.prototype.__proto__ === Object.prototype; // true
    Function.prototype instanceof Object; // true
    Function.prototype instanceof Function; // false

    // Object Array Function 等等構造函數繼承了Function.prototype
    Function instanceof Function; // true
    Array instanceof Function;  // true
    Object instanceof Function; // true
    Function instanceof Object; // true
複製代碼

經過上面代碼知道繼承的原型鏈大體是: Object.prototype(root)<---Function.prototype<---Function|Object|Array...

上面的會出現一個比較奇特的現象以下:

  • 第一問
Function.__proto__ === Function.prototype;
複製代碼

Function對象是否是由Function構造函數建立的實例?

  • 第二問
Function instanceof Object; // true
    Object instanceof Function; // true
    Object instanceof Object; // true
    Function instanceof Function; // true
複製代碼

爲何Function instanceof ObjecttrueObject instanceof Function也爲true,那麼他們究竟是什麼關係?

解答


先要了解清楚Function.prototypeObject構造函數以下: 迴歸規範,摘錄2點:

  • Function.prototype是個不一樣於通常函數(對象)的函數(對象)。

The Function prototype object is itself a Function object (its [[Class]] is "Function") that, when invoked, accepts any arguments and returns undefined. The value of the [[Prototype]] internal property of the Function prototype object is the standard built-in Object prototype object (15.2.4). The initial value of the [[Extensible]] internal property of the Function prototype object is true. 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像普通函數同樣能夠調用,但老是返回undefined

  • 普通函數其實是Function的實例,即普通函數繼承於Function.prototypefunc.__proto__ === Function.prototype

  • Function.prototype繼承於Object.prototype,而且沒有prototype這個屬性。func.prototype是普通對象,Function.prototype.prototypenull

  • 因此,Function.prototype實際上是個另類的函數,能夠獨立於/先於Function產生。

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

The value of the [[Prototype]] internal property of the Object constructor is the standard built-in Function prototype object. 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.

第一問

Function對象是由Function構造函數建立的一個實例?

Yes 的部分: 按照 JavaScript 中「實例」的定義,ab的實例即 a instanceof btrue,默認判斷條件就是 b.prototypea 的原型鏈上。而 Function instanceof Function 爲 true,本質上即 Object.getPrototypeOf(Function) === Function.prototype,正符合此定義。

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

我的偏向先有的Function.prototype,再有的function Function,全部構造函數本質上都是集成於Function.prototype ,因此Function.__proto__ === Function.prototype

第二問

// Function.__proto__、Function.prototype指向同一個對象,Function.prototype.__proto__指向Object.prototype
    // Function.__proto__ => Function.prototype.__proto__ => Object.prototype => Object.prototype.__proto__ => null
    Function instanceof Object; // true
    // Object做爲構造函數繼承自Function.prototype
    // Object.__proto__ => Function.prototype
    Object instanceof Function; // true
    // Object做爲構造函數繼承自Function.prototype,Function.prototype__proto__指向Object.prototype
    // Object.__proto__ => Function.prototype => Function.prototype.__proto__ => Object.prototype
    Object instanceof Object; // true
    // Function構造函數也是繼承自Function.prototype
    // Function.__proto__ => Function.prototype
    Function instanceof Function; // true
複製代碼

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

總結


  • Object.prototype是瀏覽器底層根據 ECMAScript 規範創造的一個對象。
  • Function.prototype直接繼承的Object.prototype,一樣它也是由是引擎建立出來的函數,引擎認爲不須要給這個函數對象添加 prototype 屬性。Function.prototype.prototypeundefined
  • 先有 Object.prototype(原型鏈頂端),Function.prototype 繼承 Object.prototype 而產生,最後,FunctionObject 和其它構造函數繼承 Function.prototype 而產生。

參考

MDN Object.prototype

MDN Function.prototype

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

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

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

相關文章
相關標籤/搜索