【进阶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…

我是木易杨,公众号「高级前端进阶」做者,跟着我每周重点攻克一个前端面试重难点。接下来让我带你走进高级前端的世界,在进阶的路上,共勉!