一、什么是原型和原型链
我们先来看一个简单的例子,首先我们定义一个Number()函数
var n1 = new Number(1)
这个时候我们就可以调用Number()函数的各种方法了,比如 toString()
n1.toString()
我们知道基本类型对象是存在栈内存中的;复杂类型对象存在堆内存中,栈内存中只存其地址(指针)
所以此时 n1 如果要调用 toString() 方法的话,必须有一个指针能指向这个对象。那么 toString() 这个方法存在哪里比较好呢,最基本的想法当然是存在 Number() 函数中好了
这样调用当然不会有什么问题,但是如果此时我们再来一个
var n2 = new Number(2)
同是也调用 n2 的toString()方法
n2.toString
那此时我们就发现有一个问题,n1 和 n2 都有一个toString() 方法,如果在栈内存中各自存储一个toString() 的方法的话,很明显比较浪费,当数据量变大时就会明显影响性能。
所以,我们是不是能将toString() 这类大家都有可能会用到的方法打包放在一个对象内,然后在Number() 函数放一个指针不就好了,那就给这个指针取个名字吧,就叫__proto__
吧

从图中我们可以看到,Number(1) 中的有一个预设属性
__proto__
,里面包含了 toString() 在内的好几个有关数值的方法。那无关数值的方法,有关对象的方法呢
hasOwnProperty()
,眼尖同学已经发现了,在Number() 的__proto__
中还有一个__proto__: Object
,就像你所看到的一样,这里指向的就是对象的通用方法。


这里我们就你能看到n2.__proto__
和 Number.prototype
是一样的,证明n2.__proto__
指向的就是Number.prototype
。

同理:
n2.__proto__.__proto__ === Object.prototype
也是true
这里我们就能推广得到一个比较通用的法则 对象.__proto__ = 函数.prototype
例如:
String.__proto__ === Function.prototype \\true
Number.__proto__ === Function.prototype \\true
Function.__proto__.__proto__ === Object.prototype \\true
这个时候需要给这整个做法取个名字了,由于这些个公用属性一个指向一个,要是画出箭头指针,就像链子一样,那就要原型链吧。这些指向的最后就是null。
Object.prototype.__proto__ === null //true
二、原型链解决了什么问题
我们要说到一个新词继承,依旧是 var n2 = new Number(2)
,我们没有定义n2任何的属性和方法,但是它却能够通过原型链调用Number.prototype、甚至是Object.prototype 中的属性和方法,这就是n2 继承了符合它本身数据类型的公用属性。
所以当试图访问一个对象的属性时,它不仅仅在该对象上搜寻,还会搜寻该对象的原型,以及该对象的原型的原型,依次层层向上搜索,直到找到一个名字匹配的属性或到达原型链的末尾。
网友评论