帮你彻底搞懂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 相关文章推荐
C#中TrimStart,TrimEnd,Trim在javascript上的实现
Jan 17 Javascript
使用C++为node.js写扩展模块
Apr 22 Javascript
js实现的简洁网页滑动tab菜单效果代码
Aug 24 Javascript
jQuery validate+artdialog+jquery form实现弹出表单思路详解
Apr 18 Javascript
基于JS实现二维码图片固定在右下角某处并跟随滚动条滚动
Feb 08 Javascript
canvas绘制环形进度条
Feb 23 Javascript
JavaScript使用ZeroClipboard操作剪切板
May 10 Javascript
Node.js实现文件上传的示例
Jun 28 Javascript
vue 实现左右拖拽元素并且不超过他的父元素的宽度
Nov 30 Javascript
Node.js对MongoDB进行增删改查操作的实例代码
Apr 18 Javascript
extjs图表绘制之条形图实现方法分析
Mar 06 Javascript
vue 使用 vue-pdf 实现pdf在线预览的示例代码
Apr 26 Javascript
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
PHP学习 运算符与运算符优先级
2008/06/15 PHP
php 用checkbox一次性删除多条记录的方法
2010/02/23 PHP
PHP 验证身份证是否合法的函数
2017/02/09 PHP
wordpress自定义标签云与随机获取标签的方法详解
2019/03/22 PHP
PHP实现的62进制转10进制,10进制转62进制函数示例
2019/06/06 PHP
Yii框架安装简明教程
2020/05/15 PHP
js中格式化日期时间型数据函数代码
2010/11/08 Javascript
判断用户是否在线的代码
2011/03/05 Javascript
关于二级域名下使用一级域名下的COOKIE的问题
2011/11/07 Javascript
用js代码改变单选框选中状态的简单实例
2013/12/18 Javascript
Javascript验证Visa和MasterCard信用卡号的方法
2015/07/27 Javascript
jquery如何获取元素的滚动条高度等实现代码
2015/10/19 Javascript
VC调用javascript的几种方法(推荐)
2016/08/09 Javascript
Vue.js常用指令汇总(v-if、v-for等)
2016/11/03 Javascript
React中使用UEditor百度富文本的方法
2018/08/22 Javascript
JavaScript JMap类定义与使用方法示例
2019/01/22 Javascript
JS中的算法与数据结构之集合(Set)实例详解
2019/08/20 Javascript
从零使用TypeScript开发项目打包发布到npm
2020/02/14 Javascript
AutoJs实现刷宝短视频的思路详解
2020/05/22 Javascript
Vue项目中数据的深度监听或对象属性的监听实例
2020/07/17 Javascript
Vue实现移动端拖拽交换位置
2020/07/29 Javascript
Python实现登录接口的示例代码
2017/07/21 Python
python django 增删改查操作 数据库Mysql
2017/07/27 Python
浅谈Django中view对数据库的调用方法
2019/07/18 Python
OpenCV模板匹配matchTemplate的实现
2019/10/18 Python
美体小铺瑞典官方网站:The Body Shop瑞典
2018/01/27 全球购物
法国女性内衣购物网站:Glamuse
2019/05/13 全球购物
中专生职业生涯规划书范文
2013/12/29 职场文书
推荐信模板
2014/05/09 职场文书
企业职业病防治方案
2014/05/29 职场文书
优秀应届生求职信
2014/06/16 职场文书
硕士学位论文评语
2014/12/31 职场文书
学校计划生育责任书
2015/05/09 职场文书
公司要求试用期员工提交“述职报告”,该怎么写?
2019/07/17 职场文书
Apache POI的基本使用详解
2021/11/07 Servers
css3 选择器
2022/05/11 HTML / CSS