图解JS原型和原型链实现原理


Posted in Javascript onSeptember 15, 2020

在我初学 JS 语言的继承机制原型和原型链的时候,我一直理解不了这种设计机制,再加上之前原有对 Java继承的理解,在学习 JS 继承机制的设计上踩了一个大坑,很多知识点前期都是死记硬背,无法真正的理解它的设计思想。

JS 中的继承机制思想可以说是学习 JS 的一个核心思想,更可以说是 JS 中的一个命脉,往往这些复杂、抽象的继承关系,以及专业术语、代名词确成为了困扰初学者的绊脚石。当我真正理解它的设计思想时,其实并没有那么复杂,而且觉得非常简单。

在写这篇 JS 的原型和原型链的文章之前,我在谷歌搜索检索了大量的高赞有关 JS 原型和原型链的文章,大部分都是围绕着“是什么”来讲的,导致部分初学者缺少对 JS 继承的设计与实现的前后关联性,还是很难准确的去理解。

我们先要明白,学习这块内容知识要知道设计者“ 为什么这样做 ” 远比 “怎么做的” 重要的多这才是掌握这部分内容的关键。

今天小鹿对 JS 的继承机制要做一个系统的总结,从设计者的角度出发,将复杂的设计思想用动画呈现,将零碎的知识点体系化,争取让你一文搞懂 JS 的继承机制思想(原型和原型链)。

思维导图

图解JS原型和原型链实现原理

1.JS 的发展史

要想贯彻 JS 的核心设计思想,我们要从 JS 的诞生说起。

1.1 为什么会诞生 JavaScript ?

相对比较成熟的浏览器是由网景公司发布的,早些年间,浏览器只能浏览网页内容,而不能进行用户交互。比如我们登录输入用户名和密码,在浏览器是不能进行判断用户是否真正输入了,而是通过服务器来判断,如果没有输入,返回错误提示用户,这种设计非常的浪费时间和服务器资源。

图解JS原型和原型链实现原理

为了解决这个问题,网景公司需要开发一种运行在浏览器中的脚本语言,用来简单的做用户输入校验等操作。

当时最流行的语言是面向对象的Java编程语言 ,网景公司为了能够借助 Java将浏览器脚本语言流传开,所以起名 JavaScript。其实两者没有任何的关系。

1.2 存在的问题

JS 中的数据类型设计受当时 Java流行的影响,都是对象类型,这时候就遇到问题了,有对象必然涉及到继承机制,那么 JS 的继承机制要设计成 Java一样呢?还是另有设计思想?

2.JS 继承的设计思想

JS 的开发者想如果设计成像 Java一样有“类”的概念岂不是和 Java一样成为了一种完全面向对象的编程语言了?最后决定自己设计一种继承机制,但是它的设计思想还是采用了 Java的一些特性。

2.1 生成对象

通常 Java 生成对象是通过 new 的方式,通过类生成一个实例对象的过程。但是 JS 中并没有类,那 JS 的设计者要怎么做?

图解JS原型和原型链实现原理

他找到了 Java 和 JS 的共同点就是两者都有构造函数, Java的 new 的过程内部其实调用了构造函数。但是 JS 是没有“类”的概念的,于是 JS 就把new 一个“类”设计成了 new 一个构造函数,于是构造函数成为了一个实例对象的原型对象。

图解JS原型和原型链实现原理

3.为什么要设计原型对象?

上述这样的原型设计有一个致命的缺点就是无法共享公共属性。

因为我们知道,每 new 一个对象,生成的实例是两个不同的对象。所以共有的属性也不是共享的。

图解JS原型和原型链实现原理

所以要设计一个对象专门用来存储对象共享的属性,那么我们叫它「原型对象」。

4.什么是原型对象?

要想让构造函数生成的所有实例对象都能够共享属性,那么我们就给构造函数加一个属性叫做prototype,用来指向原型对象,我们把所有实例对象共享的属性和方法都放在这个构造函数的prototype属性指向的原型对象中,不需要共享的属性和方法放在构造函数中。

这里有一点疑惑就是,我们知道对象可以设置属性,函数也可以设置属性吗?对于初学者来说是比较懵逼的,那我们可以稍微的简单说一下:

JavaScript 中的函数拥有对象的所有能力,也因此可被称作为任意其他类型对象来对待。当我们说函数是第一类对象的时候,就是说函数也能够对象的一些功能,比如添加属性,函数当做参数传递等。

所以说,实例对象一旦通过构造函数创建,就会自动给实例对象赋值上原型对象上共享的属性或方法。说清楚一点就是该对象属性都指向了原型对象的属性值。

图解JS原型和原型链实现原理

5.对象和函数在原型链关系?

图解JS原型和原型链实现原理

上述的图反映了对象以及函数在原型链中的关系,如果你觉的上边的这张图看懵逼了,没关系,我刚开始学习原型链的时候,根本不知道上边这是什么“清明上河图”,小鹿下面通过一步步的拆分讲解,看这张图就非常简单,没错,非常简单。

我们文章的开头也说了什么是原型对象,说白了就是构造函数的一个 prototype属性,这个属性就指向原型对象。

其实我们其中一些连接属性没有讲到,只讲到了prototype属性,下面一张图来将剩下的属性补充完整,我们只要把这张图印到大脑中就可以了。

图解JS原型和原型链实现原理

我们来分析一下上图,首先我们先要声明一个狗的构造函数,定义其名字和体重属性(私有属性),同时每个构造函数我们上边讲到了,都会有一个prototype属性。

图解JS原型和原型链实现原理

这个prototype指向的就是原型对象,原型对象放的就是对象共享的属性。但是注意,原型对象里有一个constructor属性,这个属性又指回了构造函数。

图解JS原型和原型链实现原理

我们通过 new 构造函数生成两个狗的对象实例,一个叫豆豆,一个叫贝贝,这两个是两个不同的对象,名字体重都不相同,但是他们会共享原型对象上的属性 type,它们共有的属性都是犬类。

图解JS原型和原型链实现原理

在 JS 所有对象中,只要是对象,都会有一个内置属性叫做_proto_,而且这个属性是系统自动生成的,只要你创建一个对象,这个对象就有这个属性。这个_proto_属性指向的是原型对象。

通过上边的分布讲解,我们明白了构造函数与对象实例以及原型对象的关系。

总结为一句话为:

构造函数的 prototype 指向原型对象,原型对象有一个 constructor 属性指回构造函数,每个构造函数生成的实例对象都有一个 _proto_ 属性,这个属性指向原型对象。

没错,原型就是这么简单。但是你会发现,原型也是对象呀,你说只要是对象都会有一个_proto_属性指向自身构造函数的原型对象。

没错,要想知道原型对象的_proto_属性指向谁,就要知道是哪个构造函数创建了原型对象?

我们知道,所有的 JS 对象的都是继承了一个叫做 Object 的对象。可以理解为Object 构造函数创造了这个万物,他们的关系如下,和上边是同样的道理,上边总结的那句话好好理解一下。

图解JS原型和原型链实现原理

但是上图中会有一个疑问,Object 构造函数原型对象的也是对象,它肯定也有一个_proto_属性,为什么会指向 null 呢?

我们在拿上述总结的那句话,_proto_属性指向的是自身构造函数的原型对象,自身的构造函数是谁?是 Object 构造函数,那 Object构造函数的原型是谁?当然是本身(如图),所以把_proto_指向了null。

上边的关系如果不仔细整理的话确实很乱,尤其是对于初学者,但是如果像小鹿这样已整理,再乱的关系把它安排的井井有条,没有理解,就多看几篇文章。

6.原型链

我们还有一个问题没有解决就是原型链?既然我么你知道什么是原型了,原型链是什么?顾名思义,肯定是一条链,既然每个对象都有一个_proto_属性指向原型对象,那么原型对象也有_proto_指向原型对象的原型对象,直到指向上图中的null,这才到达原型链的顶端。

不要忘了,上边那种图我们还没有把它理解,我们把图自上而下理解。

图解JS原型和原型链实现原理

第一张图分解,上边小鹿画的图的关系和这个一样的,仔细对比一下,很简单,第一张图就这么解决了。

图解JS原型和原型链实现原理

我们继续向下分割,看第二张图。

图解JS原型和原型链实现原理

第二张图怎么还是那么眼熟呢,这不是小鹿上边分析的 Object 的关系图吗?对的,没错。

图解JS原型和原型链实现原理

第三张图,稍微绕个弯子,但是换汤不换药呀,听小鹿分析来。

图解JS原型和原型链实现原理

看着还是眼熟,只不过把function换成了Function,f 变成了大写的 F,这里涉及到一个知识点就是,在 JS 中,所有的 function函数都是由Function继承来的,可以说是Function是所有 function的祖宗。

那Function是由谁生产来的?我们看到图中的Function函数有_proto_属性了,而且属性指向自己的原型对象,那不就是自己繁衍自己吗?可以这么理解。

小结

这里我们在纵观全图,总结几条定义你比对着图去找。

1、所有的实例的_proto_都指向该构造函数的原型对象(prototype)。

2、所有的函数(包括构造函数)是Function的实例,所以所有函数的 _proto_的都指向Function的原型对象。

3、所有的原型对象(包括 Function的原型对象)都是Object的实例,所以_proto_都指向 Object(构造函数)的原型对象。而 Object构造函数的 _proto_指向 null。

4、Function构造函数本身就是 Function 的实例,所以_proto_指向Function的原型对象。

全篇文章的精华都在最后的总结部分,前边的所有分解讲解是为了让你理解这些函数对象以及原型对象之间的关系,这关系都是固定的,谁指向谁,都是写死额,只要你记住了他们的关系,这张图就理解的差不多了,能够理解完这张图,你的原型和原型链已经了解的很扎实了,但是还需要做一些面试题巩固一下。

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

Javascript 相关文章推荐
Dojo 学习笔记入门篇 First Dojo Example
Nov 15 Javascript
Javascript 网页水印(非图片水印)实现代码
Mar 01 Javascript
php对mongodb的扩展(初识如故)
Nov 11 Javascript
网站如何做到完全不需要jQuery也可以满足简单需求
Jun 27 Javascript
JavaScript使用setInterval()函数实现简单轮询操作的方法
Feb 02 Javascript
深入浅析javascript立即执行函数
Oct 23 Javascript
jQuery实现的placeholder效果完整实例
Aug 02 Javascript
ionic实现可滑动的tab选项卡切换效果
Apr 15 Javascript
详解springmvc 接收json对象的两种方式
Dec 06 Javascript
vue-property-decorator用法详解
Dec 12 Javascript
jQuery实现聊天对话框
Feb 08 jQuery
Openlayers测量距离与面积的实现方法
Sep 25 Javascript
vue实现简单全选和反选功能
Sep 15 #Javascript
vscode 调试 node.js的方法步骤
Sep 15 #Javascript
js实现特别简单的钟表效果
Sep 14 #Javascript
仿照Element-ui实现一个简易的$message方法
Sep 14 #Javascript
vue swipeCell滑动单元格(仿微信)的实现示例
Sep 14 #Javascript
JavaScript 如何计算文本的行数的实现
Sep 14 #Javascript
JavaScript实现串行请求的示例代码
Sep 14 #Javascript
You might like
php cli 小技巧
2013/06/03 PHP
解析PHP中如何将数组变量写入文件
2013/06/06 PHP
FastCGI 进程意外退出造成500错误
2015/07/26 PHP
Yii2中DropDownList简单用法示例
2016/07/18 PHP
javascript与CSS复习(三)
2010/06/29 Javascript
通过javascript获取iframe里的值示例代码
2013/06/24 Javascript
jQuery的控件及事件(输入控件及回车事件)使用示例
2013/07/25 Javascript
我的Node.js学习之路(二)NPM模块管理
2014/07/06 Javascript
jQuery表单域选择器用法分析
2015/02/10 Javascript
jQuery解析XML与传统JavaScript方法的差别实例分析
2015/03/05 Javascript
JavaScript将字符串转换为整数的方法
2015/04/14 Javascript
jQuery Ajax中的事件详细介绍
2015/04/16 Javascript
实现placeholder效果的方案汇总
2015/06/11 Javascript
Node.js中JavaScript操作MySQL的常用方法整理
2016/03/01 Javascript
angularjs实现文字上下无缝滚动特效代码
2016/09/04 Javascript
Node.js设置CORS跨域请求中多域名白名单的方法
2017/03/28 Javascript
vue.js之vue-cli脚手架的搭建详解
2017/05/05 Javascript
JavaScript实现离开页面前提示功能【附jQuery实现方法】
2017/09/26 jQuery
express如何使用session与cookie的方法
2018/01/30 Javascript
JavaScript回调函数callback用法解析
2020/01/14 Javascript
Jquery高级应用Deferred对象原理及使用实例
2020/05/28 jQuery
Python代码的打包与发布详解
2014/07/30 Python
Python中subprocess的简单使用示例
2015/07/28 Python
django 修改server端口号的方法
2018/05/14 Python
Python3实现的简单三级菜单功能示例
2019/03/12 Python
Python生成MD5值的两种方法实例分析
2019/04/26 Python
12个步骤教你理解Python装饰器
2019/07/01 Python
python实现日志按天分割
2019/07/22 Python
Flask之pipenv虚拟环境的实现
2019/11/26 Python
使用CSS3配合IE滤镜实现渐变和投影的效果
2015/09/06 HTML / CSS
瑞典在互联网上最大的宠物商店:Animail
2020/10/31 全球购物
建筑专业毕业生自荐信
2014/05/25 职场文书
县委常委班子对照检查材料思想汇报
2014/09/28 职场文书
2014年党务工作总结
2014/11/25 职场文书
《世界多美呀》教学反思
2016/02/22 职场文书
解决xampp安装后Apache无法启动
2022/03/21 Servers