定义
每个实例对象( object )都有一个私有属性(称之为 __ proto __ )指向它的构造函数的原型对象(prototype )。该原型对象也有一个自己的原型对象( __ proto __ ) ,层层向上直到一个对象的原型对象为 null。根据定义,null 没有原型,并作为这个原型链中的最后一个环节。
认识原型
首先介绍一下 prototype 这个概念, 其实每个函数都会有 prototype 属性, 说到原型肯定离不开 prototype ,那么 prototype 属性到底指向的是什么呢? 让我们来看一段代码
function init(){}
init.prototype.name = 'huxiaolei'
let init1 = new init()
let init2 = new init()
console.log(init1.name) // huxiaolei
console.log(init2.name) // huxiaolei
如代码所展示, 函数的 prototype 指向了一个对象, 这个对象正是这个函数创建的实例的原型, 也就是 init1 和 init2 的原型.
OK, 现在我们来看看定义的第一句话 "每个实例对象( object )都有一个私有属性(称之为 __ proto __ )指向它的构造函数的原型对象(prototype )" , 在这个定义中提到了一个属性, __ proto__, 那么就是说构造函数的prototype和实例对象的原型是同一个, 那么可以在代码中打印一下是否一样.
console.log(init.prototype === init1.__proto__) // true
console.log(init.prototype === init2.__proto__) // true
验证成功, 是一样的! 图形整理如下
所以到现在为止我们已经了解什么事原型了, 那么原型链是怎么回事呢?
原型链
从字面意思来理解, 原型链就是由一系列的原型连接起来形成一条链. 当然这只是字面上的意思, 那么原型链真的概念是定义的第二句话, "...该原型对象也有一个自己的原型对象( __ proto __ ) ,层层向上直到一个对象的原型对象为 null。根据定义,null 没有原型,并作为这个原型链中的最后一个环节。"
根据定义我们可以知道, 原型对象也有自己的原型对象, 所以当我们去读取实例的属性时, 如果找不到的话, 就会查找与对象关联的原型中的属性, 如果还查不到, 就会去找原型的原型, 一直找到最顶层为止.
function init(){}
init.prototype.name = 'xiaolei'
let init1 = new init()
init1.name = 'hu'
console.log(init1.name) // hu
delete init1.name;
console.log(init1.name) // xiaolei
如上例所示, 我们给 init1 添加一个name属性, 第一次自然打印 'hu'.
那么当我们删掉name这个属性的时候, 在 init1 就找不到了, 这个时候就会去 init1 的原型中去找, 也就是 init.prototype 中去找, 所以结果为 'xiaolei'.
那么原型的原型到底是什么呢, 原型链的最后是什么呢?
我们都称原型为原型对象, 所以原型自然就是一个对象, 那么我们就可以用对象的方式来创建它.
let obj = new Object()
obj.name = 'xiaolei'
console.log(obj.name) // xiaolei
其实原型对象就是通过 Object 构造函数生成的,结合之前所讲,实例的 proto 指向构造函数的 prototype
那么原型链的概念已经很清楚了, 文字表达如下, 图见最后.
实例对象的__proto__ --> 构造函数的prototype --> 构造函数的prototype的__proto__ --> ... --> null
等等, 这个地方为什么最后指向了null, null是怎么来的? 别急, 我们来打印一下Object.prototype.__ proto __ 是什么
所以null代表的是没有对象, 就不用继续向上查找了. 自然就是作为了原型链中的最后一个环节.
最后附上原型链的图, 蓝色的线就是原型链.
本文章图均由冴羽的博客提供