本文首发于微信公众号「IT自学课堂」,公众号IT:ItSelfStudyClass
1. 前言
原型与原型链是JavaScript中的核心概念之一,也是初学者的噩梦,同时面试时被问到的概率大概在80%以上!
通过这个问题,可以考察应聘者对JavaScript基础知识的理解与掌握程度。原型链的知识通过死记硬背是记不住的,就算今天记住,明天准忘了。
但是,当你真正理解原型链的概念之后,你会发现,原型链竟然如此简单,而且很难忘记。
最近两个月都在招人面试,面了有30+,不管对方是初级、高级还是资深级别,这道题必问。但是真正理解原型链的人不到3个。
很多人能够说准确说出原型链的概念,但是你让他针对具体的实例写出对应的原型链,能写对的人寥寥无几。
所以今天准备写一篇关于原型链的文章,和大家分享一下我对原型链的理解,如有错误的地方,欢迎大家探讨。
2. 原型与原型链的概念
我们来看一下MDN对原型与原型链的解释:
当谈到继承时, JavaScript 只有一种结构:对象。每个实例对象( object )都有一个私有属性(称之为 __proto__ )指向它的 构造函数的原型对象( prototype)。该原型对象也有一个自己的原型对象( __proto__ ) ,层层向上直到一个对象的原型对象为null
。根据定义,null
没有原型,并作为这个 原型链中的最后一个环节。
上面有个概念,我们务必记住:
- 每个实例对象都有一个私有属性:__proto__ ,该私有属性 指向实例构造函数的原型对象。
3. 原型与原型链图解
所谓原型”链“,不管是现实中的锁链,还是计算机算法中的链表,他们都是把一个一个节点串联起来,从而形成一条链。
在JavaScript中,原型链也是如此。原型对象是节点,而私有属性 __proto__ 就是串联连接两个原型对象节点的”线“。
例如,有一个超类(SupType)和子类(SubType),子类通过原型继承超类,并创建一个子类的实例对象 instance,具体代码如下:
function SupType() {
this.supProperty = true;
this.name = 'SupType';
}
SupType.prototype.getSupValue = function() {
return this.supProperty;
}
function SubType() {
this.subProperty = false;
}
SubType.prototype = new SupType();
SubType.prototype.constructor = SubType;
SubType.prototype.getSubValue = function() {
return this.subProperty;
}
SubType.prototype.getName = function() {
return this.name;
}
const instance = new SubType();
按照上面我们对“务必记住的概念”的理解,instance 对象的 __proto__ 指向实例(instance)构造函数原型对象:SubType.prototype,图解如下:
SubType.prototype 是超类 SupType 的实例,也有一个 _proto 属性,所以SubType.prototype.__proto__指向 SubType.prototype 对象构造函数的原型对象:SupType.prototype,图解如下:
SupType.prototype 也有一个 __proto__ 属性,那 SupType.prototype 对象又是哪个构造函数的实例对象呢?
答案是 Object,所以 SupType.prototype.__proto__ 指向的是 Object.prototype 对象。那Object.prototype 又是哪个构造函数的实例对象呢?是 null 对象,无中生有,原型链的终点。
图解如下:
用代码表达,这条原型链如下:
instance.__proto__ === SubType.prototype
SubType.prototype.__proto__ ==== SupType.prototype
SupType.prototype.__proto__ === Object.prototype
Object.prototype.__proto__ === null
4. 其他
在JavaScript中,引用类型和基本包装类型可以通过构造函数创建实例对象,这些实例的原型链与Object创建实例的原型链类似,最终也是指向 null 对象,原型链完整版图解如下:
5. 总结
- 每一个实例对象都有一个私有属性 __proto__ ,该私有属性总是指向实例构造函数的原型对象;
- 不同的原型对象”节点“通过 __proto__ 指向进行串联连接,从而形成一条原型链。
- 原型链的终点为 null 对象,即无中生有。