原型链
原型是什么?
在JavaScript中的构造函数都有一个属性prototype,叫做原型,这是在构造函数定义的时候就会自动创建这个属性,这个属性是指向一个空的Object(这里的空Object含义:是我们本身没有给它增加属性,但是是可以增加属性的)。
在JavaScript的实例对象中会有一个__proto__属性,这是在创建实例的时候JS自动添加的属性,__proto__的指向是创建它的构造函数的prototype属性,该属性不适合生产环境使用,可用于测试方面。
通过代码来验证prototype和__proto__的关系:
上面的Fun就是一个构造函数,通过new关键字创建实例fun。分别打印出实例的__proto__和构造函数的prototype,通过判断是true,说明__proto__全等于创建它的构造函数的prototype属性。
原型链
原型链是一种关系,是实例对象和原型对象之间的关系,这种关系是通过原型(proto)来联系的。原型的顶端是就是Object.prototype,但是最顶端是null。
JavaScript访问对象属性(方法),就是通过原型层层往上寻找,如果访问的是属性,没有访问到就返回undefined,如果是方法访问不到就报错。
通过画图来了解原型链的关系:
代码结果图:
原型链继承图:
图中左边是栈空间,保存的是引用数据类型的引用地址,右边的是堆空间。
第一步:首先定义一个构造函数Fn,这时候在堆空间创建Fn函数对象,同时JS自动生成一个prototype属性,该属性指向一个Object空对象,这个Object空对象也是原型Object的实例,是实例就存在__proto__属性,且该属性是指向Object构造函数的prototype(也就是所有对象的原型顶端)
第二步:通过Fn.prototype.test2=function,在Fn的prototype属性(prototype就是一个空的Object对象)上添加一个方法test2()。
第三步:通过new关键字创建fn实例,这时候会指向 Fn中的this.test1=function,在fn实例对象本身中产生一个test1方法,同时fn实例对象自动生成一个属性__proto__属性,该属性指向创建它的构造函数的prototype属性,意思就是fn(实例)的__proto__属性和Fn(构造函数)的prototype属性是同一个。
Object对象是JS一开始就存在的,Object.prototype就是所有原型的顶端
原型链寻找的步骤和层级关系
调用fn.test1()方法,会自动的从fn实例本身对象中寻找,找到就返回。
调用fn.test2()方法,先找本身实例fn中是否含有,没有就寻找fn.proto,因为通过Fn.prototype.test2创建了该方法 ,fn.__ptoto__就是指向的Fn.ptototype,所以在Fn.prototype中找到再返回。
调用fn.toString(),同样找实例本身,再找创建它的构造函数的prototype,如果找不到,因为prototype也是实例,只不过是Object原型顶端的实例,所有通过Fn.ptototype.__proto__找到最顶端,这时toString就存在原型最顶端,所以调用成功。
调用fn.test3(),同样找实例本身,再找创建它的构造函数的prototype,如果找不到,因为prototype也是实例,只不过是Object原型顶端的实例,所有通过Fn.ptototype.__proto__找到最顶端,在原型最顶端也不存在,所以调用失败,寻找的是函数所以报错。