深入理解JavaScript系列(1):原型与原型链
原型
构造函数创建对象
我们来写一个构造函数用来创建一个对象
1 | function Person() {} |
从例子中,我们使用 new
创建了一个实例对象 person
。
这我们都是可以理解的,new
创建实例对象。
prototype
每一个函数都有一个 prototype
属性,那么这个属性指向什么呢?
其实,函数的 prototype
属性指向了一个对象,这个对象就是调用该构造函数而创建的实例的原型,也就是例子中,person
的原型。
我们来打印一下 Person
的 prototype
,来看看是什么。
1 | function Person() {} |
说了这么多,还是不知道什么是原型(0_o)。我们可以这样来理解原型:每一个 JS 对象(null 除外)在创建的时候就会有一个与之关联的另一个对象,而这个对象就是我们所说的原型,每一个对象都会从原型”继承”属性。
我们简单来表示一下构造函数与实例原型之间的关系:
我们从这张图中,可以看出,Object.prototype
表示实例原型。
那么我们该怎么表示实例(person
)与实例原型(Person.prototype
)之间的关系呢?所以我们来看看第二个属性
__proto__
这是每一个 JS 对象(除了 null
) 都具有的一个属性,叫 __proto__
,这个属性会指向该对象的原型。
我们来验证一下。
1 | function Person() {} |
注意: 当然,在这里我们要提一嘴,__proto__
在 web
标准中,以及删除,但是一些浏览器目前还是支持的。
所以在这里,我顺便提一个
Object.getPrototypeOf(Object)
,用来获取实例的原型。
于是,我们可以将图进行更新了:
我们现在,构造函数指向原型,实例指向原型,那么原型能否指向构造函数或者指向实例呢?
其实这时候我们就要提到下一个属性了,但是,指向实例的是没有的,因为一个构造函数可以生成多个实例。
constructor
这是我们提到的第三个属性了,而这个属性,就是用来实现由原型指向构造函数,每一个原型都会有一个 constructor
属性来指向关联的构造函数。
1 | function Person() {} |
所以我们继续更新关系图:
到目前为止,我们已经得出来一些结论了。
1 | function Person() {} |
了解了构造函数、实例原型和实例之间的关系后,我们来看看实例和原型的关系:
实例与原型
在我们读取实例的属性时,如果找不到,就会查找与对象关联的原型中的属性,如果还查不到,就会去找原型的原型,直到最顶层为止。
1 | function Person() {} |
从例子来看,我们给原型对象添加了 name
属性,然后在给实例对象添加了 name
属性,而首先打印时,打印了实例对象的 name
。
但是当我们删除了实例对象的 name
属性时,在实例中就找不到该属性,则会从实例的原型是那个去查找,所以在 Person.prototype
中找到。
但是,如果我们的原型中没找到呢?原型的原型又是什么呢?
原型的原型
原型是什么?其实原型也是一个对象,既然是对象,那我们就可以用 new Object()
来创建它:
1 | let obj = new Object(); |
而我们再来看一下,Person.prototype 与 Object 的关系.
1 | function Person() {} |
其实原型对象就是通过 Object
构造函数生成的,实例的 __proro__
指向构造函数的 prototype
,现在我们再来更新一下关系图:
原型链
其实,看完原型的原型,还是会有疑问,Object.prototype 的原型是什么呢?
其实就是 null
,我们可以来验证一下:
1 | console.log(Obejct.prototype.__proto__ === null); |
而 null
是什么呢?
null 就是表示”没有对象”,即在这里不应该有值。
因为 Object.prototype.__proto__
的值为 null
。所以查找属性的时候其实查到 Object.prototype
就可以停止了。
我们在来更新一下关系图:
那什么是原型链呢?相互关联的原型组成的链状结构就是原型链,也就是这条红色的线。
补充
你以为这就结束了吗?
我们来看看在来补充一些神奇的事情,我们来看看打印。
1 | function Person() {} |
经过测试我们会发现,都会 true
。
这意味着什么呢?
Person
函数的原型是Function.prototype
Function.prototype
也是Object
构造函数的__proto__
Function.prototype.__proto__
就是Object.prorotype
于是,我们的图就可以更新为如下关系图:
至此,我们的关系图已经基本完善。
再次,我也需要补充一张找到的图,应该说,真的很好,因为我自己画的比较乱。(0_o)