帮你彻底搞懂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 跨浏览器开发经验总结(五) js 事件
May 19 Javascript
jQuery解决下拉框select设宽度时IE 6/7/8下option超出显示不全
May 27 Javascript
Javascript获取HTML静态页面参数传递值示例
Aug 18 Javascript
javascript生成随机大小写字母的方法
Feb 20 Javascript
JS中attr和prop属性的区别以及优先选择示例介绍
Jun 30 Javascript
javascript实现简单的html5视频播放器
May 06 Javascript
js实现多行文本框统计剩余字数功能
Mar 28 Javascript
ES6学习之变量的两种命名方法示例
Jul 18 Javascript
使用3D引擎threeJS实现星空粒子移动效果
Sep 13 Javascript
vue的过滤器filter实例详解
Sep 17 Javascript
原生js添加一个或多个类名的方法分析
Jul 30 Javascript
重置Redux的状态数据的方法实现
Nov 18 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
使用eAccelerator加密PHP程序
2008/10/03 PHP
PHP 长文章分页函数 带使用方法,不会分割段落,翻页在底部
2009/10/22 PHP
php实现复制移动文件的方法
2015/07/29 PHP
Zend Framework教程之Zend_Config_Xml用法分析
2016/03/23 PHP
Zend Framework入门教程之Zend_Session会话操作详解
2016/12/08 PHP
javascript一点特殊用法
2008/05/28 Javascript
Jquery实现的tab效果可以指定默认显示第几页
2013/10/16 Javascript
JS实现表单验证功能(验证手机号是否存在,验证码倒计时)
2016/10/11 Javascript
node.js缺少mysql模块运行报错的解决方法
2016/11/13 Javascript
详谈$.data()的用法和作用
2017/02/13 Javascript
浅谈通过JS拦截 pushState和replaceState事件
2017/07/21 Javascript
浅析node Async异步处理模块用例分析及常用方法介绍
2017/11/17 Javascript
修改Nodejs内置的npm默认配置路径方法
2018/05/13 NodeJs
JavaScript使用math.js进行精确计算操作示例
2018/06/19 Javascript
在小程序中使用Echart图表的示例代码
2018/08/02 Javascript
AngularJs1.x自定义指令独立作用域的函数传入参数方法
2018/10/09 Javascript
微信小程序自定义tabBar组件开发详解
2020/09/24 Javascript
python 打印直角三角形,等边三角形,菱形,正方形的代码
2017/11/21 Python
Python实现比较扑克牌大小程序代码示例
2017/12/06 Python
使用Python通过win32 COM实现Word文档的写入与保存方法
2018/05/08 Python
使用pandas对两个dataframe进行join的实例
2018/06/08 Python
深入浅析Python 中 is 语法带来的误解
2019/05/07 Python
python tkinter控件布局项目实例
2019/11/04 Python
python yield和Generator函数用法详解
2020/02/10 Python
python对一个数向上取整的实例方法
2020/06/18 Python
python能自学吗
2020/06/18 Python
纯CSS3实现绘制各种图形实现代码详细整理
2012/12/26 HTML / CSS
Nº21官方在线商店:numeroventuno.com
2019/09/26 全球购物
亿阳信通股份有限公司C#笔试题
2016/12/06 面试题
房地产融资计划书
2014/01/10 职场文书
司马光教学反思
2014/02/01 职场文书
学生会个人自荐书范文
2014/02/12 职场文书
文化宣传方案
2014/03/13 职场文书
委托公证书样本
2015/01/23 职场文书
碧霞祠导游词
2015/02/09 职场文书
nginx rewrite功能使用场景分析
2022/05/30 Servers