帮你彻底搞懂JS中的prototype、__proto__与constructor(图解)


Posted in Javascript onAugust 23, 2019

提示:不要排斥,静下心来,认真读完,你就搞懂了!(可以先看一下最后的总结部分再回过头来完整看完)

1. 前言

作为一名前端工程师,必须搞懂JS中的prototype__proto__constructor属性,相信很多初学者对这些属性存在许多困惑,容易把它们混淆,本文旨在帮助大家理清它们之间的关系并彻底搞懂它们。这里说明一点,__proto__属性的两边是各由两个下划线构成(这里为了方便大家看清,在两下划线之间加入了一个空格:_ _proto_ _),实际上,该属性在ES标准定义中的名字应该是[[Prototype]],具体实现是由浏览器代理自己实现,谷歌浏览器的实现就是将[[Prototype]]命名为__proto__,大家清楚这个标准定义与具体实现的区别即可(名字有所差异,功能是一样的)。本文基于谷歌浏览器(版本 72.0.3626.121)的实验结果所得。

现在正式开始! 让我们从如下一个简单的例子展开讨论,并配以相关的图帮助理解:

function Foo() {...};
let f1 = new Foo();

以上代码表示创建一个构造函数Foo(),并用new关键字实例化该构造函数得到一个实例化对象f1。这里稍微补充一下new操作符将函数作为构造器进行调用时的过程:函数被调用,然后新创建一个对象,并且成了函数的上下文(也就是此时函数内部的this是指向该新创建的对象,这意味着我们可以在构造器函数内部通过this参数初始化值),最后返回该新对象的引用。虽然是简简单单的两行代码,然而它们背后的关系却是错综复杂的,如下图所示:

帮你彻底搞懂JS中的prototype、__proto__与constructor(图解)

看到这图别怕,让我们一步步剖析,彻底搞懂它们!

图的说明:右下角为图例,红色箭头表示__proto__属性指向、绿色箭头表示prototype属性的指向、棕色实线箭头表示本身具有的constructor属性的指向,棕色虚线箭头表示继承而来的constructor属性的指向;蓝色方块表示对象,浅绿色方块表示函数(这里为了更好看清,Foo()仅代表是函数,并不是指执行函数Foo后得到的结果,图中的其他函数同理)。图的中间部分即为它们之间的联系,图的最左边即为例子代码。

2. _ _ proto _ _ 属性

首先,我们需要牢记两点:①__proto__constructor属性是对象所独有的;② prototype属性是函数所独有的。但是由于JS中函数也是一种对象,所以函数也拥有__proto__constructor属性,这点是致使我们产生困惑的很大原因之一。上图有点复杂,我们把它按照属性分别拆开,然后进行分析:

帮你彻底搞懂JS中的prototype、__proto__与constructor(图解)  

第一,这里我们仅留下 __proto__ 属性,它是对象所独有的,可以看到__proto__属性都是由一个对象指向一个对象,即指向它们的原型对象(也可以理解为父对象),那么这个属性的作用是什么呢?它的作用就是当访问一个对象的属性时,如果该对象内部不存在这个属性,那么就会去它的__proto__属性所指向的那个对象(可以理解为父对象)里找,如果父对象也不存在这个属性,则继续往父对象的__proto__属性所指向的那个对象(可以理解为爷爷对象)里找,如果还没找到,则继续往上找…直到原型链顶端null(可以理解为原始人。。。),再往上找就相当于在null上取值,会报错(可以理解为,再往上就已经不是“人”的范畴了,找不到了,到此结束,null为原型链的终点),由以上这种通过__proto__属性来连接对象直到null的一条链即为我们所谓的原型链。

3. prototype属性

第二,接下来我们看 prototype 属性:

帮你彻底搞懂JS中的prototype、__proto__与constructor(图解)

prototype属性,别忘了一点,就是我们前面提到要牢记的两点中的第二点,它是函数所独有的,它是从一个函数指向一个对象。它的含义是函数的原型对象,也就是这个函数(其实所有函数都可以作为构造函数)所创建的实例的原型对象,由此可知:f1.__proto__ === Foo.prototype,它们两个完全一样。那prototype属性的作用又是什么呢?它的作用就是包含可以由特定类型的所有实例共享的属性和方法,也就是让该函数所实例化的对象们都可以找到公用的属性和方法。任何函数在创建的时候,其实会默认同时创建该函数的prototype对象。

4. constructor属性

最后,我们来看一下 constructor 属性:

帮你彻底搞懂JS中的prototype、__proto__与constructor(图解)  

constructor属性也是对象才拥有的,它是从一个对象指向一个函数,含义就是指向该对象的构造函数,每个对象都有构造函数(本身拥有或继承而来,继承而来的要结合__proto__属性查看会更清楚点,如下图所示),从上图中可以看出Function这个对象比较特殊,它的构造函数就是它自己(因为Function可以看成是一个函数,也可以是一个对象),所有函数和对象最终都是由Function构造函数得来,所以constructor属性的终点就是Function这个函数。

帮你彻底搞懂JS中的prototype、__proto__与constructor(图解)

感谢网友的指出,这里解释一下上段中“每个对象都有构造函数”这句话。这里的意思是每个对象都可以找到其对应的constructor,因为创建对象的前提是需要有constructor,而这个constructor可能是对象自己本身显式定义的或者通过__proto__在原型链中找到的。而单从constructor这个属性来讲,只有prototype对象才有。每个函数在创建的时候,JS会同时创建一个该函数对应的prototype对象,而函数创建的对象.__proto__ === 该函数.prototype,该函数.prototype.constructor===该函数本身,故通过函数创建的对象即使自己没有constructor属性,它也能通过__proto__找到对应的constructor,所以任何对象最终都可以找到其构造函数(null如果当成对象的话,将null除外)。如下:

帮你彻底搞懂JS中的prototype、__proto__与constructor(图解)

5. 总结

总结一下:

  • 我们需要牢记两点:①__proto__constructor属性是对象所独有的;② prototype属性是函数所独有的,因为函数也是一种对象,所以函数也拥有__proto__constructor属性。
  • __proto__属性的作用就是当访问一个对象的属性时,如果该对象内部不存在这个属性,那么就会去它的__proto__属性所指向的那个对象(父对象)里找,一直找,直到__proto__属性的终点null,再往上找就相当于在null上取值,会报错。通过__proto__属性将对象连接起来的这条链路即我们所谓的原型链。
  • prototype属性的作用就是让该函数所实例化的对象们都可以找到公用的属性和方法,即f1.__proto__ === Foo.prototype
  • constructor属性的含义就是指向该对象的构造函数,所有函数(此时看成对象了)最终的构造函数都指向Function。

本文就此结束了,希望对那些对JS中的prototype__proto__constructor属性有困惑的同学有所帮助。

最后,感谢这两篇博文,本文中的部分内容参考自这两篇博文:
https://3water.com/article/168334.htm
https://3water.com/article/89523.htm

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
通过javascript的匿名函数来分析几段简单有趣的代码
Jun 29 Javascript
简单的Jquery遮罩层代码实例
Nov 14 Javascript
javascript中动态加载js文件多种解决办法总结
Nov 15 Javascript
js实现模拟计算器退格键删除文字效果的方法
May 07 Javascript
JS实现的手机端精简幻灯片效果
Sep 05 Javascript
浅谈react.js 之 批量添加与删除功能
Apr 17 Javascript
微信小程序使用modal组件弹出对话框功能示例
Nov 29 Javascript
jQuery实现鼠标滑过商品小图片上显示对应大图片功能【测试可用】
Apr 27 jQuery
对Vue beforeRouteEnter 的next执行时机详解
Aug 25 Javascript
LayUI数据接口返回实体封装的例子
Sep 12 Javascript
jQuery 添加元素和删除元素的方法
Jul 15 jQuery
vue实现下载文件流完整前后端代码
Nov 17 Vue.js
JS 实现发送短信验证码的“59秒后重新发送验证短信”功能
Aug 23 #Javascript
微信小程序swiper禁止用户手动滑动代码实例
Aug 23 #Javascript
Node.JS枚举统计当前文件夹和子目录下所有代码文件行数
Aug 23 #Javascript
微信小程序之侧边栏滑动实现过程解析(附完整源码)
Aug 23 #Javascript
微信小程序之下拉列表实现方法解析(附完整源码)
Aug 23 #Javascript
Vue中消息横向滚动时setInterval清不掉的问题及解决方法
Aug 23 #Javascript
微信小程序 授权登录详解(附完整源码)
Aug 23 #Javascript
You might like
杏林同学录(四)
2006/10/09 PHP
推荐一篇入门级的Class文章
2007/03/19 PHP
memcache一致性hash的php实现方法
2015/03/05 PHP
PHP 接入微信扫码支付总结(总结篇)
2016/11/03 PHP
php socket通信简单实现
2016/11/18 PHP
jQuery+php简单实现全选删除的方法
2016/11/28 PHP
实例分析PHP中PHPMailer发邮件
2017/12/13 PHP
JavaScript 原型与继承说明
2010/06/09 Javascript
jQuery中 noConflict() 方法使用
2013/04/25 Javascript
php实例分享之实现显示网站运行时间
2014/05/20 Javascript
使用jquery prev()方法找到同级的前一个元素
2014/07/11 Javascript
一个css与js结合的下拉菜单支持主流浏览器
2014/10/08 Javascript
基于BootStrap与jQuery.validate实现表单提交校验功能
2016/12/22 Javascript
详解React Native顶|底部导航使用小技巧
2017/09/14 Javascript
Mac下安装vue
2018/04/11 Javascript
原生js实现的移动端可拖动进度条插件功能详解
2019/08/15 Javascript
vue中js判断长时间不操作界面自动退出登录(推荐)
2020/01/22 Javascript
Vue使用vue-draggable 插件在不同列表之间拖拽功能
2020/03/12 Javascript
[49:43]VG vs FNATIC 2019国际邀请赛小组赛 BO2 第一场 8.15
2019/08/17 DOTA
linux 下实现python多版本安装实践
2014/11/18 Python
使用Python读写文本文件及编写简单的文本编辑器
2016/03/11 Python
一个基于flask的web应用诞生 组织结构调整(7)
2017/04/11 Python
python斐波那契数列的计算方法
2018/09/27 Python
python global关键字的用法详解
2019/09/05 Python
手把手教你从PyCharm安装到激活(最新激活码),亲测有效可激活至2089年
2020/11/25 Python
HTML5中外部浏览器唤起微信分享功能的代码
2020/09/15 HTML / CSS
大学生村官事迹材料
2014/01/21 职场文书
优秀民警事迹材料
2014/01/29 职场文书
小学英语教学反思
2014/01/30 职场文书
小学运动会加油词
2015/07/18 职场文书
关于国庆节的广播稿
2015/08/19 职场文书
观看《信仰》心得体会
2016/01/15 职场文书
2019年房屋委托租赁合同范本(通用版)!
2019/07/17 职场文书
抖音短视频(douyin)去水印工具的实现代码
2021/03/30 Javascript
Python手拉手教你爬取贝壳房源数据的实战教程
2021/05/21 Python
Windows和Linux上部署Golang并运行程序
2022/04/22 Servers