最近在重读小红书《JavaScript 高级程序设计 第三版》,同时将自己觉得有启发的点记录下来,你可以去这里看我对每一章节的记录note-for-litter-red-book。这几天看到第六章《面向对象的程序设计》,觉得有必要将原型链重新梳理一下,因此有了这篇文章。

构造函数、实例、原型这场三角恋

在说原型链之前,有必要先说明构造函数、实例、原型之间的关系。在使用构造函数创建实例的时候,就会涉及到这三个概念,举个栗子:

1
2
3
4
5
6
7
function Person(name){
this.name = name;
}
Person.prototype.getName = function(){
console.log(this.name);
}
var person = new Person('sunyuhui');

在上面的栗子中,通过构造函数Person生成了person这个实例。同时我们在Person的原型Person.prototype上声明了getName这个方法。

这场三角恋之间的关系如下图(点击查看大图):

三角恋

用文字来说就是:构造函数prototype属性指向构造函数的原型构造函数的原型constructor属性指向构造函数构造函数生成的实例__proto__属性指向构造函数的原型

我们来验证一下:

chrome中实践一下

如果你之前对原型链不太熟悉,那上面的图你可能会有点迷糊,建议你将上面两张图结合起来看,一定要把这三者之间的关系理清楚,这是理解原型链的基础。

OK,上面的两张图理解了吗?如果没有,建议你再看几遍,直到理解了再往下面看。

何为原型链

好,现在要动真格的了,在上面的栗子中,实例person继承了构造函数的原型Person.prototypegetName方法。

那如果构造函数的原型Person.prototype也是继承了另外一个构造函数的原型呢?

好,我们已经接触到原型链的本质了,不明白的话,缓一缓,舒口气,我们将上面的栗子扩充一下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function Animal(planet){
this.planet = planet;
}
Animal.prototype.getPlanet = function(){
return this.planet;
}

function Person(name){
this.name = name;
}

Person.prototype = new Animal('earth'); // 没有这一句,Animal和Person豪不相干,有了这一句,就实现了继承。


Person.prototype.getName = function(){
return this.name;
}
var person = new Person('sunyuhui');

上面加注释的那条语句是将构造函数的原型Person.prototype赋值为构造函数Animal生成的实例,好像说的有点绕了。

说的更具体点:实例person继承了构造函数的原型Person.prototype的方法,那如果构造函数的原型Person.prototype也是一个实例呢?那她也就能继承另外一个原型的方法了,在这个例子中,构造函数的原型Person.prototype就是构造函数Animal生成的实例,这个实例也就继承了构造函数Animal的原型Animal.prototype的方法了。

如下图:

原型链的继承

光说不练假把式,我们来实践看看。

继承的栗子

OK,关于原型链到这里就结束了,大家有什么问题可以去关于找到我的联系方式。