-
Notifications
You must be signed in to change notification settings - Fork 1.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
【进阶5-3期】深入探究 Function & Object 鸡蛋问题 #35
Comments
好文, 1、用 C/C++ 构造内部数据结构创建一个 FP 即 (Function.prototype) 以及初始化其内部属性但不包括行为。 理由: |
这个说的并不是很充分啊: 打印 Object.prototype.constructor === Object 输出的是 true 啊 |
打印Object.prototype.toString(Function.prototype)输出[object Object] ,因此,Function.prototype 的 [[Class]] 属性是 Object。 Function.prototype 不仅是一个对象,还是一个匿名函数。 console.log(Function.prototype); //=> ƒ () { [native code] } |
你理解是错误的,楼主是对的 |
现在翻回来看,突然发现是new 实例,我确实写错了,谢谢纠正哈! |
引言
上篇文章用图解的方式向大家介绍了原型链及其继承方案,在介绍原型链继承的过程中讲解原型链运作机制以及属性遮蔽等知识,今天这篇文章就来深入探究下
Function.__proto__ === Function.prototype
引起的鸡生蛋蛋生鸡问题,并在这个过程中深入了解 Object.prototype、Function.prototype、function Object 、function Function 之间的关系。Object.prototype
我们先来看看 ECMAScript 上的定义(15.2.4)。
Object.prototype 表示 Object 的原型对象,其
[[Prototype]]
属性是 null,访问器属性__proto__
暴露了一个对象的内部[[Prototype]]
。 Object.prototype 并不是通过Object
函数创建的,为什么呢?看如下代码实例对象的
__proto__
指向构造函数的prototype
,即f.__proto__
指向 Foo.prototype,但是Object.prototype.__proto__
是 null,所以 Object.prototype 并不是通过 Object 函数创建的,那它如何生成的?其实 Object.prototype 是浏览器底层根据 ECMAScript 规范创造的一个对象。Object.prototype 就是原型链的顶端(不考虑 null 的情况下),所有对象继承了它的 toString 等方法和属性。
Function.prototype
我们先来看看 ECMAScript 上的定义(15.3.4)。
Function.prototype 对象是一个函数(对象),其
[[Prototype]]
内部属性值指向内建对象 Object.prototype。Function.prototype 对象自身没有valueOf
属性,其从 Object.prototype 对象继承了valueOf
属性。Function.prototype 的
[[Class]]
属性是Function
,所以这是一个函数,但又不大一样。为什么这么说呢?因为我们知道只有函数才有 prototype 属性,但并不是所有函数都有这个属性,因为 Function.prototype 这个函数就没有。当然你会发现下面这个函数也没有 prototype 属性。
为什么没有呢,我的理解是
Function.prototype
是引擎创建出来的函数,引擎认为不需要给这个函数对象添加prototype
属性,不然Function.prototype.prototype…
将无休无止并且没有存在的意义。function Object
我们先来看看 ECMAScript 上的定义(15.2.3)。
Object 作为构造函数时,其
[[Prototype]]
内部属性值指向 Function.prototype,即使用
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 属性。function Function
我们先来看看 ECMAScript 上的定义(15.3.3)。
Function 构造函数是一个函数对象,其
[[Class]]
属性是Function
。Function 的[[Prototype]]
属性指向了Function.prototype
,即到这里就有意思了,我们看下鸡生蛋蛋生鸡问题。
Function & Object 鸡蛋问题
我们看下面这段代码
Object
构造函数继承了Function.prototype
,同时Function
构造函数继承了Object.prototype
。这里就产生了 鸡和蛋 的问题。为什么会出现这种问题,因为Function.prototype
和Function.__proto__
都指向Function.prototype
。对于
Function.__proto__ === Function.prototype
这一现象有 2 种解释,争论点在于 Function 对象是不是由 Function 构造函数创建的一个实例?解释 1、YES:按照 JavaScript 中“实例”的定义,a 是 b 的实例即
a instanceof b
为 true,默认判断条件就是b.prototype
在 a 的原型链上。而Function instanceof Function
为 true,本质上即Object.getPrototypeOf(Function) === Function.prototype
,正符合此定义。解释 2、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 环境时都发生了什么?
1、用 C/C++ 构造内部数据结构创建一个 OP 即 (Object.prototype) 以及初始化其内部属性但不包括行为。
2、用 C/C++ 构造内部数据结构创建一个 FP 即 (Function.prototype) 以及初始化其内部属性但不包括行为。
3、将 FP 的
[[Prototype]]
指向 OP。4、用 C/C++ 构造内部数据结构创建各种内置引用类型。
5、将各内置引用类型的[[Prototype]]指向 FP。
6、将 Function 的 prototype 指向 FP。
7、将 Object 的 prototype 指向 OP。
8、用 Function 实例化出 OP,FP,以及 Object 的行为并挂载。
9、用 Object 实例化出除 Object 以及 Function 的其他内置引用类型的 prototype 属性对象。
10、用 Function 实例化出除Object 以及 Function 的其他内置引用类型的 prototype 属性对象的行为并挂载。
11、实例化内置对象 Math 以及 Grobal
至此,所有内置类型构建完成。
参考
文章穿梭机
【进阶5-2期】图解原型链及其继承优缺点
【进阶5-1期】重新认识构造函数、原型和原型链
【进阶4-4期】Lodash是如何实现深拷贝的
【进阶4-3期】面试题之如何实现一个深拷贝
【进阶4-2期】Object.assign 原理及其实现
【进阶4-1期】详细解析赋值、浅拷贝和深拷贝的区别
进阶系列目录
交流
进阶系列文章汇总如下,觉得不错点个Star,欢迎 加群 互相学习。
我是木易杨,公众号「高级前端进阶」作者,跟着我每周重点攻克一个前端面试重难点。接下来让我带你走进高级前端的世界,在进阶的路上,共勉!
The text was updated successfully, but these errors were encountered: