合肥凯发网址直营
科技有限公司

18955110833

原型和原型链,你真的理解吗?

栏目: 技术学堂 查看()

原型和原型链,你真的理解吗?

JavaScript中,原型(prototype原型链(prototype chain是非常重要的概念,但是很多人对它们的掌握都有点不太清楚,本文带大家彻底搞懂它们!

 

非常重要的基本概念

首先我们需要搞清楚几个非常重要的基本概念:

所有的对象都是通过new 函数创建的,且所有的对象都是引用类型

image.png

如上,字面量{}只是创建对象的一种语法糖,其实上面的两种写法是完全等价的。而Object其实就是一个js内置的构造函数。

 

所有的函数也都是对象,都是通过new Function创建的(可以称为函数对象)

image.png

既然函数也都是对象。那么函数又是怎么创建的呢?其实函数都是通过new Function创建的,而且所有的对象都是通过new 函数创建的,又称为函数对象。

如上。同理可得,上面的两种写法也是完全等价的。而Object既然是一个函数、它其实就是相当于var Object = new Function(),所以它也不例外。

 

思考:其实Function也是一个函数对象,那它又是怎么创建的呢?

结论:Function函数是JS内置的,不需要创建!

综上所述,可以用一张图来形象的展示出它们的关系:

image.png

原型prototype

什么是原型?

所有的函数都有一个属性叫prototype,称之为函数原型

 

image.png

 

可以运行如上代码看看效果,ObjectArray其实都是js内置的构造函数。

 

默认情况下,prototype是一个普通的object对象

image.png

通过这个特点、这个后面会讲到,我们可以在原型链上查找实例化后的对象所对应的构造函数。

 

隐式原型__proto__

什么是隐式原型?

所有的对象都有一个属性叫__proto__,称之为隐式原型

image.png

 

可以运行如上代码看看效果,所有的对象在被创建的时候就会有一个叫__proto__的属性。

默认情况下,__proto__指向创建该对象的函数的原型,即prototype

 

image.png

运行如上代码,可以验证结论。在这里其实就已经初步形成了一种三角关系了。

image.png

原型链prototype chain

到重点了,上面讲了原型和隐式原型,估计有的人会问:搞这么麻烦,它们有什么用呢?

image.png

如上代码,我通过构造函数User实例化了两个对象user1和user2,所以user1和user2的sayHello方法是不相等的,因为它们都有各自属性和方法。

但是我们可以发现。完全没必要创建多次,它们的方法其实是一样的。可以假设一下。如果我们创建了很多个示例,那么这样就会浪费内存空间。

现在让我们来改下代码

image.png

 

先看结果,这时候我们会发现,代码依然能够如期运行,而且user1user2sayHello方法是完全相等的!

那为什么user1user2中并没有sayHello这个属性,但是却能够运行呢?其实这就是原型链的强大之处。

 

那这个到底是怎么实现的呢?原型链它到底是什么呢?

 

image.png

运行如上代码,需要理解以下几点:

首先根据上面所说的,user1是个实例化的对象,那么user1.proto一定是指向User.prototype

那User.prototype其实也是个对象,既然是对象就有proto属性,那么User.prototype.proto一定是指向Object.prototype,也可以通过user1.proto.proto来访问,即原型对象的原型对象

同理,那user1.proto.proto也是个对象,也有proto属性,不过这里就指向null了

因为每个一对象都有隐式原型,隐式原型的指向就形成了一个链条,称之为原型链

所以上面的示例就会按照原型链继续依次查找

当我们访问一个对象的成员时:

首先看该对象自身是否拥有该成员,如果有直接使用;

如果没有再看该对象的隐式原型是否拥有该成员,如果有直接使用;

如果还没有就会继续查找原型对象的原型对象(该对象的隐式原型所指向的原型对象),直到找到为止;

这样形成的链条就被称为原型链(prototype chain)。

 

读到这里,再看上面的问题,为什么实例对象user1user2中并没有sayHello这个方法,但是却能够运行?答案就是可以在原型链上查找到sayHello这个方法。

通过这种方法。我们就可以实现多个实例对象共享一个原型对象,这样就可以极大的减少对内存空间的消耗且极大的提升了代码的可复用性。这就是原型链的作用。(其实这也是js实现继承的基本原理,这里就不展开说了)

核心:原型链的全貌

下面就是js原型链的全貌了

image.png

这里有两个比较特殊的点需要注意一下:

Objectprototype__protp__指向null

Function__protp__指向自身的prototype

仔细对照这张图,你是否能够理解如下问题:

为什么任意一个对象都能调用toString和hasOwnProperty方法了吗?

为什么任意一个函数都能调用call和toString方法了吗?

为什么任意一个数组都能调用自身没有的api了吗,如filter,push等方法了吗?

其实答案都是因为原型上有!

如果你能够看到这里并且完全理解了,那么其实你已经对原型和原型链已经有了一个比较全面的认知了。


扫二维码与项目经理沟通

我们在微信上24小时期待你的声音

解答本文疑问/技术咨询/运营咨询/技术建议/互联网交流

郑重申明:凯发网址直营 以外的任何单位或个人,不得使用该案例作为工作成功展示!