帮你彻底搞懂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 相关文章推荐
循环 vs 递归浅谈
Feb 28 Javascript
jQuery插件ajaxfileupload.js实现上传文件
Oct 23 Javascript
解析JavaScript面向对象概念中的Object类型与作用域
May 10 Javascript
浅析javascript中的Event事件
Dec 09 Javascript
bootstrap组件之导航组件使用方法
Jan 19 Javascript
Vue中建立全局引用或者全局命令的方法
Aug 21 Javascript
Angular4 ElementRef的应用
Feb 26 Javascript
webpack热模块替换(HMR)/热更新的方法
Apr 05 Javascript
讲解vue-router之什么是编程式路由
May 28 Javascript
对angular 监控数据模型变化的事件方法$watch详解
Oct 09 Javascript
vue 动态表单开发方法案例详解
Dec 02 Javascript
react中useState使用:如何实现在当前表格直接更改数据
Aug 05 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编码规范之注释和文件结构说明
2010/07/09 PHP
php中的三元运算符使用说明
2011/07/03 PHP
Laravel手动分页实现方法详解
2016/10/09 PHP
php导出csv文件,可导出前导0实例代码
2016/11/16 PHP
浅谈Yii乐观锁的使用及原理
2017/07/25 PHP
PHP面向对象中new self()与 new static()的区别浅析
2017/08/17 PHP
javascript 隐藏/显示指定的区域附HTML元素【legend】用法
2010/03/05 Javascript
javascript 二维数组的实现与应用
2010/03/16 Javascript
Ext.get() 和 Ext.query()组合使用实现最灵活的取元素方式
2011/09/26 Javascript
js Math 对象的方法
2013/09/01 Javascript
JavaScript的事件绑定(方便不支持js的时候)
2013/10/01 Javascript
js实现交换运动效果的方法
2015/04/10 Javascript
JS随机调用指定函数的方法
2015/07/01 Javascript
jQuery+HTML5加入购物车代码分享
2020/10/29 Javascript
微信小程序 缓存(本地缓存、异步缓存、同步缓存)详解
2017/01/17 Javascript
详解JavaScript中js对象与JSON格式字符串的相互转换
2017/02/14 Javascript
详解A标签中href=""的几种用法
2017/08/20 Javascript
微信小程序中setInterval的使用方法
2017/09/29 Javascript
JS实现仿微信支付弹窗功能
2018/06/25 Javascript
微信小程序webview 脚手架使用详解
2019/07/22 Javascript
javascript数组元素删除方法delete和splice解析
2019/12/09 Javascript
JavaScript事件冒泡机制原理实例解析
2020/01/14 Javascript
[00:35]可解锁地面特效
2018/12/20 DOTA
操作Windows注册表的简单的Python程序制作教程
2015/04/07 Python
pandas series序列转化为星期几的实例
2018/04/11 Python
Python针对给定字符串求解所有子序列是否为回文序列的方法
2018/04/21 Python
python微信聊天机器人改进版(定时或触发抓取天气预报、励志语录等,向好友推送)
2019/04/25 Python
Python常用库大全及简要说明
2020/01/17 Python
python中requests模拟登录的三种方式(携带cookie/session进行请求网站)
2020/11/17 Python
阿迪达斯印度官方商城:adidas India
2017/03/26 全球购物
英国最大的美妆产品在线零售商之一:Beauty Bay
2017/09/29 全球购物
StubHub希腊:购买体育赛事、音乐会和剧院门票
2019/08/03 全球购物
英国领先的在线礼品店:Getting Personal
2019/09/24 全球购物
《湘夫人》教学反思
2014/02/21 职场文书
预防传染病方案
2014/06/14 职场文书
超市七夕促销活动方案
2014/08/28 职场文书