图解:什么是 JS 原型和原型链?

 图解:什么是 JS 原型和原型链?

本文转载自微信公众号「小鹿动画学编程」,作者不甘平凡的码农。转载本文请联系小鹿动画学编程公众号。 

写在前边图解:什么是 JS 原型和原型链?

在我初学 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_属性了,而且属性指向自己的原型对象,那不就是自己繁衍自己吗?可以这么理解。

小结

图解:什么是 JS 原型和原型链?

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

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

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

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

相关推荐