object htmldivelement什么意思_深入探究 Function amp; Object 鸡蛋问题

7644e596a519446108a6856e9e47aac2.png

(给前端树加星标,提升前端技能)

转自:高级前端进阶

引言

上篇文章用图解的方式向大家介绍了原型链及其继承方案,在介绍原型链继承的过程中讲解原型链运作机制以及属性遮蔽等知识,今天这篇文章就来深入探究下 Function.__proto__ === Function.prototype 引起的鸡生蛋蛋生鸡问题,并在这个过程中深入了解 Object.prototype、Function.prototype、function Object 、function Function 之间的关系。

Object.prototype

我们先来看看 ECMAScript 上的定义(15.2.4)。

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.

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 等方法和属性。

9001947e8dbe2c2c182f1e1be716e2dc.png

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 属性。

a2f99b05b36b88d3d19cf21e0748729d.png

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

9e7269110dd17a1868d4271d669994b9.png

使用 new Object() 创建新对象时,这个新对象的 [[Prototype]] 内部属性指向构造函数的 prototype 属性,对应上图就是 Object.prototype。

fb81fa3d03802ce336c1ce791c160c81.png

当然也可以通过对象字面量等方式创建对象。

  • 使用对象字面量创建的对象,其 [[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 ---> nulllet a = ["yo", "whadup", "?"];
// 原型链:    a ---> Array.prototype ---> Object.prototype ---> nullfunction f(){return 2;
}
// 原型链:    f ---> Function.prototype ---> Object.prototype ---> nullvar fun = new Function();
// 原型链:    fun ---> Function.prototype ---> Object.prototype ---> nullfunction Foo() {return {};
}
let foo = new Foo();
// 原型链:    foo ---> Foo.prototype ---> 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

9ba6cce73615a79e99e306941650412d.png

到这里就有意思了,我们看下鸡生蛋蛋生鸡问题。

Function & Object 鸡蛋问题

我们看下面这段代码

Object instanceof Function         // true
Function instanceof Object         // trueObject 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 构造函数创建的一个实例?

解释 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 上的方法。

最后给一个完整的图,看懂这张图原型就没问题了。

a0d0ad403f52144573cc4cd39a63807f.png

内置类型构建过程

JavaScript 内置类型是浏览器内核自带的,浏览器底层对 JavaScript 的实现基于 C/C++,那么浏览器在初始化 JavaScript 环境时都发生了什么?

没找到官方文档,下文参考自 https://segmentfault.com/a/1190000005754797。对于其观点比较认同,欢迎小伙伴提出不同观点。

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

至此,所有内置类型构建完成。

参考

从探究Function.__proto__===Function.prototype过程中的一些收获
高能!typeof Function.prototype 引发的先有 Function 还是先有 Object 的探讨
从__proto__和prototype来深入理解JS对象和原型链

觉得本文对你有帮助?请分享给更多人

关注「前端树」加星标,提升前端技能

d917e6e7b1c139e77d33cd077022b70c.png

喜欢就点一下「在看」呗~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/561146.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何拉取k8s镜像_K8s 从懵圈到熟练 – 镜像拉取这件小事

导读:相比 K8s 集群的其他功能,私有镜像的自动拉取,看起来可能是比较简单的。而镜像拉取失败,大多数情况下都和权限有关。所以,在处理相关问题的时候,我们往往会轻松的说:这问题很简单&#xff…

hadoop没有datanode_Hadoop运行在Kubernetes平台实践

Hadoop与Kubernetes就好像江湖里的两大绝世高手,一个是成名已久的长者,至今仍然名声远扬,一个则是初出茅庐的青涩少年,骨骼惊奇,不走寻常路,一出手便惊诧了整个武林。Hadoop与Kubernetes之间有很深的渊源&a…

git 修改commit_Git从8到13 深入了解Git特性

上一章简单的介绍了一些常用的Git命令,这一章主要是深入了解一下Git的稍微高级的一些知识和指令。8.首先我们先来通过查看Git目录里面的文件来深入了解一下Git版本控制的构造。 查看HEAD可以知道当前所在的分支。在config文件里面存储着Git里面的一些配置信息&#…

eclipse maven打包jar 部分jsp无法访问_Maven系列教材 (九)- 在Eclipse中创建maven风格的java web项目...

Maven系列教材 (九)- 在Eclipse中创建maven风格的java web项目步骤1:删除j2ee目录步骤2:新建Maven 项目步骤3:这个界面点下一步步骤4: 这个界面使用webapp,点下一步 步骤5:这一步填写如图所示的信息步骤6:此时得到的maven web 项目的问题步骤…

final关键字_深入分析Java中的final关键字

Java中被final修饰的变量与普通变量有何区别?被final修饰的变量不可更改、被final修饰的方法不可重写是怎样做到的?带着疑问我们一点点拨开云雾。一、final的内存定义及规则对于final关键字,编译器、处理器从读写两个角度限制了其使用规则&am…

嵌入式和fpga哪个好前景_Java 和 go 哪个就业前景好一点?面向金钱编程,这个回答太现实...

有个朋友问我:ava 和 go 哪个就业前景好一点?现在纠结中,差不多一年半就要毕业了。我也问了几个朋友程序员朋友:朋友A: 可以参考 Boss 直聘,面向金钱编程,我身边的 Java 都是 15k 左右,反正干就…

php解决mysql主从同步_Mysql读写分离,主从同步实现

随着用户量的增多,数据库操作往往会成为一个系统的瓶颈所在,因此我们可以通过实现数据库的读写分离来提高系统的性能。通过设置主从数据库实现读写分离,主库负责“写”操作,从库负责“读”操作,根据压力情况&#xff0…

验证码图片显示不出来怎么办_pr 的蒙版不显示了怎么办?

小白自学pr一路会遇到很多坑,我也是在一个个坑里爬过的。画好的蒙版不显示了,这只能算是一个小坑。解决方法很简单,只需要记住一条:在pr里想要调整或者显示什么东西,首先必须要选中它。举例说明:我给小猫的…

gb2312编码表_汉字编码输入系统模型(一)

通过前面介绍的通信系统模型来分析汉字编码输入系统,将汉字编码输入系统的特殊性整合到通信系统模型中,从而建立起一种基于信息论的汉字编码输入系统模型(参见图3.2),以便指导我们的汉字编码输入实践,设计和…

java中sql语句怎么把开始和结束时间作为参数写sql查询_JDBC数据库连接怎么操作?...

之前一直听说过JDBC,但从来不知道它是何物的小伙伴们看过来啦!一、概述JDBC(Java Data Base Connectivity,java数据库连接)是一种用于执行SQL语句的Java API,可以为多种关系数据库提供统一访问,它由一组用Java语言编写的类和接口组…

spring boot整合shiro继承redis_spring-boot-plus集成Shiro+JWT权限管理

SpringBootShiroJWT权限管理ShiroApache Shiro是一个强大且易用的Java安全框架,执行身份验证、授权、密码和会话管理。使用Shiro的易于理解的API,您可以快速、轻松地获得任何应用程序,从最小的移动应用程序到最大的网络和企业应用程序。三个核心组件:Subject, Secur…

java 整数变负数_一文帮你读懂Java整数的存储原理

前言大家应该都知道,整数包括负数,零,和正数。在Java中,基本类型中byte(8位)、short(16位)、int(32位)、long(64位)属于整数,并且没有无符号数,均是有符号的。对于计算机来说,它只认识二进制&am…

server2019 sqlcmd命令安装_Ubuntu20.04LTS安装MS sql-server2019的方法

在本文中,我将向您展示如何在Ubuntu 20.04 Focal Fossa上安装Microsoft SQL Server。小广告:喜欢网络技术的朋友可以加W信:xfiles_sky一起学习进步。步骤一:更新ubuntu源sudo apt update && sudo apt upgrade步骤二&#…

servlet里面为什么有时候覆_为什么新来的经理强烈推荐?前后端分离知识,学到了...

引言前后端分离这个词相信大家都听过,不知道大家是怎么理解的呢。前阵子看项目的时候,有一段实现硬是没看懂,下面来给大家说一下一段愚蠢的经历哈。(我没正正式式写过前端,所以如果文章有错的地方希望可以在评论区友善交流~)一、交…

microstation添加txt文件_C开发实战-文件操作

文件概述文件几乎无处不在,主要分为磁盘文件和设备文件,典型的磁盘文件有文本文件和二进制文件,磁盘文件存储在外部存储介质(例如磁盘,硬盘,U盘等等)需要加载到内存中才能使用。无论是文本文件还是二进制文件在计算机内…

怎样配置mysql数据源_mysql怎样配置ODBC数据源

选中 sqlserver选择默认连接的数据库 7、配置完成,可以测试连接 扩展资料: spring中配置数据源的几种常见方式:工具/原料 事先配置相应的环境mysql(mysql安装程序)mysql-connector-odbc-3.51.20-win32.exe(mysql数据源dobc安装程序) 步骤/方法…

python字符串用法_笔记:python字符串的使用

Python 没有表示单个字符的字符类型,只有字符串类型str。字符串是用单引号或双引号括起来的一系列字符。单引号表示的字符串中可以包含双引号字符,但不能直接包含单引号字符(否则无法知道字符串的开始和结尾分别在哪里)。同样,双引号表示的字…

new出来的对象怎么回收_JVM的内存模型及垃圾回收算法

1、什么是jvm:虚拟出来的计算机,是jre的一部分,使用jvm是为了支持与操作系统无关,实现跨平台,jvm内部体系结构主要分为三个部分:类加载器子系统,运行时数据区和执行引擎。2、jvm内存区域运行时数…

条令考试小程序辅助器_可以自己编题的答题软件,自定义题库考试出题工具,微信答题小程序...

自制题库自己编题的答题小程序要如何制作?今天给大家介绍下可以自己编题的答题软件,自制题库软件在微信小程序里自己出题,然后给别人来答题。每个人都可以通过这个小程序系统出考题,然后给到别人答题(自己、同事、学生…

编写有效用例电子版_软件测试人员必须编写代码吗?

相信每一个刚入门软件测试的小伙伴都会琢磨一个问题:软件测试人员,要不要写代码?其实这个问题同样困扰着已经在测试行业闯荡了几年的测试小司机们。那今天,我就来给大家分析一下~01各种软件测试角色一般而言软件测试大体可以分为两…