JavaScript原型链
要搞清楚原型链,我们首先了解什么是
原型
?每个函数都有prototype
属性,我们称之为原型
,又因为这个属性的值是一个对象,所以我们也称为原型对象
。那么什么是
对象
呢?这里我们要了解在JavaScript中对象是一种引用数据类型。我们可以通过很多种方式来创建一个对象。我们常用构造函数的方式来创建一个对象:
1 | //构造函数 |
那么,函数与对象的关系是怎么样的呢?
- 函数是对象,对象都是通过函数创建的。
- 函数与对象并不是简单的包含与被包含的关系。
在我们上面创建的对象中,通过定义常量实例化后,在原来
构造函数
中通过this
赋值的方法或者属性,已经在实例化后成为了每个实例的实例属性
和实例方法
,无法共享公共属性和方法。所以设计出了一个原型对象
,来存储这个构造函数的公共属性及方法。
我们再下面一段代码了加深一下理解:
1 | // 构造函数 |
可以看到我们实例对象实例
p1
调用到Person
这个构造函数
的原型对象
上的say()方法。但是为什么呢?明明只有在构造函数
内部通过this
来赋值的属性或者方法才会被实例所继承,为什么在构造函数
的原型对象
上定义的say
方法也能通过实例来调用到呢?这里就引出了原型链
这个概念。
实际上
原型对象
中存在的构造函数
指向了我们function创建的对象。并且每个对象都有一个__proto__
属性,这个属性会指向实例对象
的构造函数
的原型对象
,这样使得彼此之间存在一定的指向关系,能够使每个实列化对象
能够使用构造函数
的原型对象
的方法。
而这种指向关系,我们就称之为原型链。图解如下:
我们可以看到,它的
__proto__
属性指向了一个function Function
的原型对象
,该原型对象
为JS中所有函数的原型对象
,而其__proto__
属性也还是指向了function Object
的原型对象
,所以验证了原型链
的尽头为null
,这一说法。
在开发的时候,要注意不要通过
实例对象
去改变其构造函数
的原型对象
,这样会对其他通过该构造函数
生成的实例对象
造成影响。
1 | //开发中需注意点 |
原型链的尽头
我们可以看到,
实例对象
的__proto__
属性指向了一个构造函数
的原型对象
,该构造函数的原型对象
的_proto_
属性指向了Object
的原型对象
,而其Object的原型对象
的_proto_
属性指向了null,所以验证了原型链
的尽头为null
,这一说法。