壹 ❀ 引

我在JS 疫情宅在家,学习不能停,七千字长文助你彻底弄懂原型与原型链一文中介绍了JavaScript原型与原型链,以及衍生的__proto__、constructor等一系列属性。在解答了多个问题的同时,也得出了很多有趣的结论。比如我们常说JavaScript中函数是一等公民,这是因为函数扮演了创造万物的角色,原始构造函数Function创造了function fn(){}(ES5中函数与构造函数并无区别)Object()Array()Number()String()等诸多构造函数,而构造函数也拥有创造对应实例对象的能力,比如Array()生产数组,String()生产字符串,你会发现JavaScript中绝大多数的数据类型,都能找到创造自己的构造函数,所以说函数是一等公民不无道理。

但在之后的时间我发现这个结论存在部分误导性以及问题,比如:

  • 如果说Function()扮演着创世主的角色,那Function.prototype不应该是仅次于原型链顶端null的存在吗?
  • 在介绍原型链的过程中,我们知道紧接在null之下的是Object.prototypeObject.prototype的起源地位似乎比Function.prototype更早,那Object.__proto__ === Function.prototype又是怎么回事?Object与Function到底谁的起源更早,谁才是真正的创世主?
  • 我们说函数扮演着实例和构造器的双重身份,这句话虽然没错,但Function.__proto__ === Function.prototype又是怎么回事?难道Function是Function的实例?自己诞生自己?

带着这些问题,我们开始本篇文章的探讨。

贰 ❀ Function()与Function.prototype

回到文章开头,如果我们用图解关系来表示Function是创世主的结论,应该这样:

但是大家心里都清楚,Object()String()等构造器并不是真的由原始构造函数Function()创建,本质上来说,它们都由JavaScript底层实现。

我们之所以说出了这个结论,是因为这些构造器的__proto__均指向了构造器Function.prototype,取自上篇博客的代码:

var fn = function () { };

Number.__proto__ === Function.prototype //true
Number.constructor === Function //true String.__proto__ === Function.prototype //true
String.constructor === Function //true Object.__proto__ === Function.prototype //true
Object.constructor === Function //true fn.__proto__ === Function.prototype //true
fn.constructor === Function //true

让我们把图画的更精准点,应该是这样:

到这里不知道大家有没有感觉到,Function()Function.prototype其实是两个东西。我们得到的实际结论是,所有函数对象的__proto__都指向Function.prototype,包括Function()本身。

Function.__proto__ === Function.prototype //true
Function.constructor === Function //true

用图表示就是这样:

这句话至少能证明,Object()Number()甚至Function()这些构造器在诞生时,确实继承了Function.prototype,至于底层在实现中时有没有使用Function()那就不得而知。

所以单站在构造器的角度,从某种角度上说,Function.prototype确实是扮演了创世主的角色,所以说Function()是创世主这句话有一定误导性,这里我做个纠正。

说了这么多,Function.prototype又是什么?我们尝试打印:

console.log(typeof Function.prototype) // "function"

可以看到Function.prototype居然是一个函数!!!!在我印象中原型一直就是一个对象,实事如此,Function的原型就是特殊的存在。

var fn = function () {};
console.log(typeof fn.prototype) // "Object"

我们通过console.dir()展开Function.prototype这个函数的属性,如下:

为什么所有的函数,原始构造器都可以使用call()、apply()这些方法呢?原来在Function.prototype这个函数上已经绑好了这些方法,而其它函数在创建时又继承了Function.prototype这个函数,能用这些方法也是意料之内的事情了。

我们再来引用ECMAScript 15.3.4 上关于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 thevalueOf property from the Object prototype Object.

The length property of the Function prototype object is 0.

从官方说明中可以得知,Function.prototype确实是一个函数对象,它的__proto__属性又指向Object.prototype。比如Function.prototype自身其实没有valueOf方法,但它从Object原型上借用了这个方法。

我怕大家看到后面又忘记了前面的概念,这里我们画成一个图:

到这里我们又知道了并不是所有的prototype对象都是Object类型,比如Function.prototype就是一个函数。最重要的一点,Function()Function.prototype不是同一个东西,同理,Object()Object.prototype也不是同一个东西,一定要区分这两者,这对于后面我们理解Object.prototype也有帮助。

叁 ❀ Object()与Object.prototype

通过前面的分析,我们知道Object()这个构造函数在创建时继承了Function.prototype这个函数,怕你们弄混,上代码:

Object.__proto__ === Function.prototype//true

Function.prototype.__proto__又指向了Object.prototype,上代码:

Function.prototype.__proto__ === Object.prototype//true

有点绕,因为这是从官方说明中得到的信息,所以我们补充上面的图:

那么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确确实实是一个对象,它的__proto__指向null,所以站在原型链的角度,Object.prototype的起源确实比Function.prototype要早。通过上面的关系图就很清楚表示了这点。

//伪代码
Object.prototype => Function.prototype => Object() => {}

还记得文章开头提出的疑问中,我们说随便创建一个函数fn,fn.prototype.__proto__指向Object.prototype,这是因为除了Function.prototype之外,其它对象的prototype都是Object类型,这里指向Object.prototype其实也不难理解。别忘了,我们说所有对象都有__proto__属性,但函数除了有__protot__之外,还有prototype属性,而一般函数的prototype都是对象类型,所以指向Object.prototype。避免大家混淆,我们直接上代码:

var fn = function () {};
fn.__proto__ === Function.prototype;
fn.prototype.__proto__ === Object.prototype;

你看,fn__proto__属性不是指向了创建自己的构造函数Function了吗,前面我之所以提出疑问,就是将fn.prototypefn理解成了一个东西。

我们将这段理解变成图,继续补充我们之前的图:

其实说到这,文章开头提出的几个疑惑,我心中都大致有了答案。首先扮演创世主的我更倾向于是函数Function.prototype,它创造了Function(),fn(),Number(),object()等一系列构造函数,如果说Function是Function的实例,我更倾向于Function是Function.prototype的实例,毕竟这些方法都继承了Function.prototype上的方法属性。当然这个理解可能有误,我现在做的就是站在结论的角度倒推原因,只是这个原因我更容易理解。至于Object与Function谁更早出现,文中其中已经给了答案,真要说起源,对象Object.prototype确实更早一点,毕竟Function.prototype都是继承于Object.prototype

除此之外,通过本文的分析,我们知道并不是所有的prototype都是对象类型,比如Function.prototype。并不是所有的函数都有prototype属性,比如Function.prototype这个函数就没有。

肆 ❀ 总

我成功抓住了二月了的尾巴,在最后一天又更新了一篇我想写的文章。今天又是从中午十二点写到了晚上七点(中间摸了会鱼),哈哈哈,还是挺充实。这篇文章说到底我并不是太满意,其实写到最后,我解答了心中部分疑惑,也产生了新的疑惑,毕竟JavaScript不是咱们写的,大部分结论都是靠自己推测,但既然写到了这里,我也确实有了新的收获,虽然对于编程帮助可能不大,但应付面试,我想各位和我一样应该是绰绰有余了。

至于三月,我想花一部分时间研究小程序,去年脑抽写了一篇入门教程,居然还有不错的点击量,那么还是给自己定个目标,继续更新下去吧。

JavaScript知识的进阶学习还会继续,各位,一起努力吧。

另外,关于文中原型的图解,还是请参考下面这张好点,文中的图只是为了自己理清思路,一旦懂了这些知识,下面这张图就是日后复习不错的提示点。

参考

深入探究 Function & Object 鸡蛋问题

从探究Function.proto===Function.prototype过程中的一些收获

详解prototype与__proto__

JS 究竟是先有鸡还是有蛋,Object与Function究竟谁出现的更早,Function算不算Function的实例等问题杂谈的更多相关文章

  1. javascript原型深入解析2--Object和Function,先有鸡先有蛋

    1.提出两个问题: Js 的prototype和__proto__ 是咋回事? 先有function 还是先有object? 2.引用<JavaScript权威指南>的一段描述: 每个JS ...

  2. packages/wepy-web/src/wx.js 分析storage 的加载原理 wx.getStorage(OBJECT)

    是小程序实例化后 读入内存 还是每次调用从文件系统读取 https://github.com/Tencent/wepy/blob/bd0003dca2bfb9581134e1b05d4aa1d80fc ...

  3. hexo的next主题博客中加入分类页面的js,实现多级目录,并且能够点击展开,隐藏下级目录~(不知道算不算深度优化~~~)

    个人博客:https://mmmmmm.me 源码:https://github.com/dataiyangu/dataiyangu.github.io 多级标题 在自己的xxxx.md文件中做如下修 ...

  4. js获取对象属性的两种方法,object.属性名,[‘属性名’ ]

    1.通过点的方式 2.通过括号的方式 例: <input type="text" value="hello" id="text"/&g ...

  5. QQ好友状态,QQ群友状态,究竟是推还是拉? 网页端收消息,究竟是推还是拉?

    https://mp.weixin.qq.com/s/KB1zdKcsh4PXXuJh4xb_Zw 网页端收消息,究竟是推还是拉? 原创 58沈剑 架构师之路 2020-12-28   https:/ ...

  6. vue-router.esm.js:1905 TypeError: Cannot convert undefined or null to object

    环境:vue+vuex+element 报错原因 ...mapState('isDialNumber') mapState如果传递一个参数,参数必须是数组 ...mapState(['isDialNu ...

  7. Vue.js 源码学习笔记 -- 分析前准备2 -- Object.defineProperty

    解析神奇的 Object.defineProperty   几行代码看他怎么用   var a= {} Object.defineProperty( a, "b", { value ...

  8. js遍历ajax回调函数返回值中的object对象

    function printObject(obj) {      //obj = {"cid":"C0","ctext":"区县& ...

  9. 菜鸡的Java笔记 Object 类

    Object 类        Object类 的主要作用        Object 类的常用方法             内容        虽然有了对象的向上转型,可以解决了参数的统一问题,但是 ...

随机推荐

  1. 在VS Code下配置Julia

    原来尝试用Sublime text3配置Julia,但是老是会出一些问题,所以直接在VS code下配置了 1.下载Julia 2.安装,安装过程和其他得软件安装一样,可以改变安装路径 3.安装完成后 ...

  2. Equalizing by Division

    The only difference between easy and hard versions is the number of elements in the array. You are g ...

  3. C - Highways poj1751最小生成树

    The island nation of Flatopia is perfectly flat. Unfortunately, Flatopia has a very poor system of p ...

  4. 不同目录有同名proto文件情况下,protoc生成.cc/.h

    首先先参考一下别人的博客,看完了,看懂了,再回过头来看我下面说的情况. 链接 https://blog.csdn.net/CAir2/article/details/78201572 但是这个也就是基 ...

  5. CKEditor与定制

    一 开始使用 官网 基本示例: 搭建服务器(这里使用apache) 下载standard的ckeditor解压放在apache的htdocs的目录下 在htdoc下面新建index.html,写入代码 ...

  6. ubuntu-18.0.4 samba安装

    (1)安装 sudo apt-get -y install samba samba-common (2)创建一个用于分享的samba目录. mkdir /home/myshare (3)给创建的这个目 ...

  7. java 一维数组的总结笔记

    数组 1. 一位数组的声明方式 type[] array Name 或 type arrayName[];(推荐使用第二种) 错误的声明方式 //int[5] intErrorArray;错误的 // ...

  8. JavaScript之浅谈内存空间

    JavaScript之浅谈内存空间 JavaScipt 内存自动回收机制 在JavaScript中,最独特的一个特点就是拥有自动的垃圾回收机制(周期性执行),这也就意味者,前端开发人员能够专注于业余, ...

  9. PHP--关于上传文件大小的问题

    参考:https://www.cnblogs.com/jianqingwang/p/5863960.html https://blog.csdn.net/u013168253/article/deta ...

  10. mysql---3种常用引擎 和优点