前言
在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__
的线路就是我们通常所说的原型链