前言
在JavaScript语言中实际上并不存在“类”,那么它是如何实现继承、多态、实例化等特性呢。它采用了与类不同的另一种技术,将其称之为原型,并通过原型链来实现继承、多态等特性。
但是需要注意的是JavaScript的继承,与Java的继承实际上有很大的区别,在Java语言中,继承是一个拷贝过程在物理地址上发生了拷贝,而JavaScript语言是通过原型链将其”链接”在一起,没有发生实际的拷贝。
构造函数创建对象
1 | function Person() { |
在这个例子中,Person 就是一个构造函数,我们使用 new 创建了一个实例对象 person。
原型
prototype
什么是原型呢?可以这样理解:每一个JavaScript对象(null除外)在创建的时候就会与之关联另一个对象,这个对象就是我们所说的原型,每一个对象都会从原型“继承“属性。
1 | function Person() { |
这个函数的 prototype 属性到底指向的是什么呢?是这个函数的原型吗?
上面例子中的解释:
Person为构造函数person1、person2为Person的实例- 构造函数的
prototype属性指向,调用该构造函数而创建的实例的原型 Person.prototype为person1的原型person1、person2继承了原型中的name属性
用一张图解释构造函数与实例原型的关系

proto
在上面的prototype中我们详细描述了构造函数如何与实例原型联系起来,那实例如何与实例原型之间如何联系呢?
在标准浏览器中构造函数的实例都拥有一个__proto__属性,通过这个实例上的属性可以找到这个实例的原型

constructor
每个原型都有一个 constructor 属性指向关联的构造函数
1 | function Person() { |

综上我们已经得出:
1 | function Person() { |
并且我们得出了:构造函数、原型、实例之间的关系

实例与原型
当我们读取对象属性时,若我们读取的对象不存在,将会自动到对象的原型上查找属性是否存在,如果属性不存在将会到原型的原型上查找,一直会找到null为止
通过🌰我们来进行说明:
1 | function Person() { |
第一次log输出时我们得到了预期输出Daisy,随后我们通过delete关键词删除了person实例上的name属性,第二次输出是通过查询发现person实例中并不存在属性name,到person实例的原型Person.prototype上进行查找找到name属性,查询终止。
若Person.prototype上任然不存在name属性呢,将会从何处进行查询?
原型的原型
从实例与实例原型的关系里面可以知道,通过__proto__属性我们可以查询到期原型,那么原型的原型是什么呢?
通过这个🌰我们可以很清晰的知道,构造函数的原型的原型是Object.prototype,Object.prototype的原型是null
1 | function Person(){} |
通过上述的知识点我们可以构建成一幅完整的关系:

其中青色__proto__的线路就是我们通常所说的原型链