前言
广告: 最近github上新开了一个仓库[May-Nodes]( https://github.com/maycope/Ma...,包括但不限于之前面试遇到的相关数据库,计算机操作系统,Java基础知识,计算机网络以及LeetCode等算法题解等知识。届时也会整理学习使用的PDF文档与资源。有需要的小伙伴 可以点个关注和`star`。在持续更新中,总会遇到你想要的。
本篇文章是最近应公司要求学习ES6
的记录性博客,博客原型来源于B站视频源地址,主要学习和记录的是原型链部分内容,感觉老师讲的很好,所以想着以自己的理解进行一个深入的记录处理,也希望能够尽量详细的讲解清楚其中的内容,帮助大家理解这部分的知识,个人感觉这部分的内容还是蛮套娃的,所以想要完完全全明白,建议看源视频,本篇博客作为收藏,忘记时候观看。
正文
前景提要
首先在讲解本部分内容之前,先设想一个场景,有以下代码:
我们在进行打印的时候,打印出来了false,因为我们在进行构造函数实例化的时候,是会创建出来两个对象,同时对于这两个对象存放在内存中的位置也不一样,这点类似于java的面试题目== 和 equals
的区别:其实对于 Object类的equals方法来说,看过源码的都知道,底层使用到的还是 == 来判断是否相等,但是不幸的是,很多继承Object的类都会对基础的方法进行重新,最后导致的结果在很多的时候,== 判断的是地址值是否是相同的,但是equals来说,判断的是值是否相同。
function Person (name,age){
this.name=name;
this.age=age;
this.doSomething = function(){
console.log("I can do something")
}
}
var man = new Person('Tom',22);
var woman = new Person('Amy',21);
console.log(man.doSomething === woman.doSomething);
// 打印false
所以在上面的代码中,我们有一个方法,然后被实例化两次,被分配了两个不同的内存空间,这个时候使用到 == 来判断时候就会导致不一样的情况,但是我们想一下,对于一个方法,在实例化一次就需要分配给他一定的空间,这样下去空间的利用率就会很低,所以我们希望的是所有的对象使用到同一个函数,这样也会比较节约内存。
构造函数原型 prototype(原型)
对于构造函数来说,通过原型分配的函数是所有对象所共享的
什么是prototype:对于js来说,规定了每一个构造函数都有一个prototype属性,指向了另外一个对象。
但是注意的是,这个prototype其实也是一个对象,同时这个对象所具有的所有属性和方法都会被构造函数所拥有。对于prototype来说翻译过来就是原型,同时是一个对象,所以理解为对于任意的构造函数,都有其自己的原型对象
。
有什么用:这里我们知道了对于每一个构造函数都会有其的原型对象,所以就可以将那些不变的方法,直接定义在prototype对象上,这个时候,所有的实例对象也都可以共享这些属性和方法:
下面我们就可以进行进行代码的修改:
function Person (name,age){
this.name=name;
this.age=age;
}
Person.prototype.doSomething = function(){
console.log("I can do something")
}
var man = new Person('Tom',22);
var woman = new Person('Amy',21);
console.log(man.doSomething=== woman.doSomething);
// 打印true
什么是原型:
就是一个对象,但是是每一个构造函数都会拥有的对象,我们也称为原型对象。
原型的主要作用
对于方法的共享,我们可以将方法定义在原型上面,所有的实例都能够使用到这个方法,并且不需要再开辟新的空间。
对象原型
这里,我们是把方法定义在了构造函数的原型对象上面,为什么实例化之后,我们的man这个对象就能够使用呢?
是因为在我们的对象的身上也有一个对象原型,叫做 _proto _前后都有两个下划线_
对于 这个东西来说是一个属性,但是有指向的值:指向了构造函数的prototype原型对象,之所以说我们能够使用到构造函数上的方法,就是因为有对象proto原型的存在。我们来打印以下我们的man对象看看是什么情况:
可以看到对于我们的man对象来说有一个proto属性,里面存放的是一个对象。所以说虽然我们的man对象没有确切的方法,但是我有一个属性,我的属性指向了一个prototype,prototype(原型对象)里面有具体的方法。就等于说两者之间是等价的。
我们进行一个打印处理:发现结果显示true,证明是完完全全等价的。
方法的查找规则:当我们使用到man对doSomething方法进行调用的时候,若是发现有这个方法,就直接进行调用即可,若是没有这个方法,因为我们有proto(这里省略四个下划线),所以若是对于构造函数的原型对象上有这个方法,还是可以进行完美的调用:
注意:对于 proto来说 存在的意义在于 为对象的查找提供给一个方向,或者说是一条路线,但是是一个非标准属性,在开发中,不可以使用这个属性,它也只是指向原型对象prototype
下面来看一下,构造函数,构造函数的原型对象,对象实例之间的关系:
构造函数,实例,原型对象三者之间关系
无论是对于我们的原型对象,或者是说,实例的proto都有一个构造函数见下图:
那问题来了对于他们之间到底是什么关系呢?
如下图所示,就是说,构造函数有一个prototype对象,其有一个属性叫做constructor又指向了Person本身。同时对于实例化的对象的proto属性也有一个属性叫做constructor,也可以指向Person本身,但是是通过prototype指向。
原型链
Person.prototype.__proto__
我们知道的是对于 Person的prototype叫做原型对象但是只要是对象就还是会有对象的原型的概念,那它到底有没有呢,我们来进行一个测试:
发现我们prototype原型对象里的proto的constructor属性指向的是一个Object。
因为我们的proto和prototype是一一对应的,现在我们进行打印验证:
于是我们就可以得到如下的结论:对于 构造函数的原型对象里面的原型指向的是Object里面的原型对象。可能优点绕口,明白原型对象是prototype,对象原型是proto。
Object.prototype.__proto__
现在思考的是对于 Object原型对象肯定也是Object 构造函数创建出来的。 如下图:
但是Object的原型对象也是一个对象,那他的proto是什么呢,我们进行一个打印。
发现是一个null值。
于是最后的原型链为:
于是我们就有了:
对于一个普通的实例对象来说拥有属性proto
,指向了构造函数的prototype(原型对象),
构造函数的原型对象也是一个对象,也有属性proto
,指向的是Object构造函数的prototype(原型对象),
Object构造函数的原型对象也是一个对象,也有属性proto
,指向的是null。
所以以上的一条条的链就是我们所说的原型链。
所以以后在访问成员或者时候就可以按照就近原则一层层的进行数据的访问,一层层的向上查找。
后记
对于原型链大体来说就是以上的一些细节部分,访问数据的时候,还是采用就近的原则,这一点也是毋庸置疑的,在他们之间链式相关链的过程中,主要是搞清楚对于prototype是一个对象,而对象就会有proto属性的思路,就可以轻松完成对原型链的理解。