概述
JS原型链
别名:隐式原型链
作用:根据一定路径查找属性(方法)
作用举例:我们定义一个构造函数Fn,使用此构造函数创建一个对象fn1,接着使用创建的对象fn1去调用toString方法并打印,我们再使用对象fn1去调用test方法并打印,结果是第一个调用toString方法不会报错,而第二个调用test方法会报错。
代码:
<script>
function Fun(){} //构造函数
var fun1 = new Fun(); //创建对象
console.log(fun1.toString()); //[object Object]
fun1.test() //报错fun1.test is not a function
</script>
提出问题:两个方法我们对象中都没有定义,但为什么会出现一个调用成功,一个却调用失败呢?
说明:调用成功是因为当我们对象去调用toString方法时,先在自身查找是否存在此方法,如果不存在,则会沿着某路径查找下去直到尽头,此路径就是原型链,当找到原型链的尽头时,我们会找到toString方法。所以执行成功。而test方法直到找到尽头也没有找到,所以就会报错。
至于原型链的路径是怎么样的,toString方法是在哪声明的,原型链尽头又是哪,这些问题下面会介绍到。
显示原型和隐式原型
核心概念
1.基本的,每一个函数都会存在一个属性:prototype,prototype属性默认指向Object空对象,此对象既称为原型对象。
prototype属性就是我们所说的显示原型。原型中有一个属性constructor,它指向函数对象。
//我们可以打印一下函数的prototype属性
function Fun(){}
console.log(Fun.prototype);
打印结果如下:
基本的,每一个实例对象都会有一个属性:__proto__,__proto__默认指向构造函数的prototypy属性值。__proto__属性就是我们所说的隐式原型。
3.特殊的,Object对象中的__proto__指向Object的原型对象,此原型对象中已经存在了如toString()等方法。值得注意的是,Object的原型对象中的__proto__不再有指向,值为null,既为尽头。
4.原型链路径:当我们查找某属性,自身没有时,会沿着__proto__查找,直到尽头,因此原型链也称为隐式原型链。
prototype和__proto__的生命起点:prototype由函数的创建而产生,__proto__由实例对象的创建而产生。
**直接看核心概念会很懵,接下来对着核心概念看如下的内存解析图会清楚很多。
内存说明
内存解析图一
根据以上核心概念,理解下方的内存中指向。
记住:函数中有prototype,对象中有___proto__
解读:首先创建了一个Fn函数,语句function Fn(){},可以看作var Fn = new Function();
所以首先在右边的堆空间中开辟了一块空间,也就是图中的A块,返回一个引用地址值0x123返回给栈空间的Fn,根据核心概念,函数中会有prototype属性存在,而prototype会指向一个
Object空对象,接下来我们有Fn函数创建了一个fn对象,语句var f1 = new Fn();所以会在内存中开辟一块空间,也就是图中的B块,根据核心概念实例对象中会有__proto__属性,指向构造函数的prototype,最终也会指向Object空对象块。
此图仅说明了函数和实例对象的内存指向,并未指向尽头。
内存解析图二
解读:首先创建了一个构造函数Fn,在其中添加了一个方法test1,并且由Fn创建了一个对象fn,这和上面的内存解析相同,但是有不同的是,在其中有语句:Fn.prototype.test2=function(){log...},此语句是在Fn的原型对象中添加了test2方法,由图就可以看出来。值得注意的是,可以看到Object函数,此函数是一开始就存在了,引用地址是0x456,Object函数对象中存在显示原型prototype,但我们知道,一般的函数的prototype属性指向的是Object的一个空对象,但是,Object函数的prototype是一个特殊,他指向的是Object的一个实例对象,此实例对象中存在了许多内置方法,此实例对象中的__proto__值为null,既为原型链尽头。
关于原型链的属性问题
在上面的演示和说明中,都是以方法来说明,那么关于原型链操作属性时是什么情况呢?
其实是有些区别的。
原型链操作属性情况
读取对象的属性时,自身没有时依然会自动到原型链中查找。
设置对象的属性时,当自身没有此属性时,就会为对象直接添加此属性。
方法一般定义的原型中,属性一般通过构造函数定义在对象本身。
总结(原型链查找过程说明)
原型链过程:使用一个构造函数创建了一个对象,使用此对象调用某属性(方法),先会在自身中查找是否定义了此属性(方法),如果找到则返回,如果没有找到,则会通过隐式原型__proto__地址值找到构造函数的prototype属性值,然后就会找到指向的原型对象(原始为空的object对象),查找此原型对象是否存在此属性(方法),如果存在则引用,如不存在,在继续通过原型对象的__proto__找到Object的prototype属性,prototype会找Object的原型对象,在其中查找,有则引用,没有则返undefind,因为当Object原型对象中也没有时,就会接着找__proto__,但是我们知道Object原型对象中的__proto__值为null,所以就可以说达到了尽头。