10分钟带你了解原型链
先上图,展示一下大概的脉络,接下来会对这张图进行讲解。
那么原型链究竟是什么?
打个比方,我们创建一个Parent函数即function Parent(),假设我们想用new Parent()创建一个或者很多个实例,暂时先把这些实例称为儿子,我们本来可以在父函数上定义参数然后用this.prop=param,把参数值赋给属性的形式实现传参赋值,但是如果有很多儿子,它们共享有一个属性或者一个方法,比如物种,它的值都为“人”,或者一个是人就有的操作,比如吃饭,即一个eat的方法,那我们一个一个给儿子添加这些它们共同享有的方法,未免也太麻烦了。所以如果这些儿子在创建出来的时候就有kind(种类)这个属性就好了。为了满足这个需要呢,js就为每个构造函数设置了一个prototype,我们暂且称这是构造函数Parent的原型,在prototype中定义一个parent构造出来的对象中都可以拥有(访问)的kind属性和eat方法,这时kind则称为原型属性,而eat则称为原型方法,由此我们可以知道protype的本意是为了能让原型对象的构造函数的实例能够共享某些属性或者方法而诞生的。但其实这些属性和方法并不是构造函数的实例所拥有的。那么这些构造函数的实例要怎么访问这些属性呢,用_proto_,即parent()的实例._proto_.name。
根据以上的逻辑,假设有构造函数P,在P.prototype上设置属性,如P.prototype.kind = “human” 然后创建一个实例 var c = new P(),最后用c._proto_.kind取到P。prototype.kind的值,所以有P.prototype=new P()._proto_。
只要是对象就有__proto__属性,通过__proto__去访问它的父对象。我们用构造函数的实例(对象)访问构造函数的prototype,那么也可以说构造函数的prototype也是一个对象,也就是构造函数的实例的父对象,那么这个父对象是不是也可以通过_proto__访问它的父对象呢,答案是可以的。所以构造函数的prototype._ptoto__也是一个对象,即Parent.prototype._proto,我们在创建对象的时候也有用new Object(),所以Object也是一个构造函数,那么它是否也有一个原型对象prototype呢,有的,这个原型对象就是Object.prototype。其实函数也是对象的一种,可以说Function=new Object(),所以我们刚才说到的Function.prototype._ptoto,它其实是Object().Prototype,所以Object().prototyppe=Function.prptotype.__prototype。我们称它为GOD,因为它是万物之主,而god.__proto__就是null了。
先举个栗子让大家消化一下:
回到刚才,我们刚说到Parent(),那么parent()是怎么来的呢,js并没有一种数据类型是Parent吧,所以它不是数据类型的一种,他是函数的一种,所以说到底它就是由Function构造出来的,把Function成为parent的构造器,我们称parent的construtor。constructor是对象才有的属性,它是从一个对象指向一个函数的。指向的函数就是该对象的构造函数。函数也是对象的一种,所以这不冲突。所以假设有a=function (){},b=new a();c=new b();我们就可以说c.construtor=b,b.construtor=a。从Function.construtor=Function,从Object.construtor = Function可知,Function是所有对象的根构造器。这就是以下这张图的由来,我们把这种种关系成为原型链。
总结
1、prototype、_proto_、constructor
- __proto__属性是对象(包括函数)独有的。__proto__属性是从一个对象指向另一个对象,即从一个对象指向该对象的原型对象(也可以理解为父对象)。显然它的含义就是告诉我们一个对象的原型对象是谁
- prototype是函数独有的属性,它从一个函数指向一个对象,代表这个对象是这个函数的原型对象,这个对象也是当前函数所创建的实例的原型对象。
- constructor是对象才有的属性,它是从一个对象指向一个函数的。指向的函数就是该对象的构造函数。
- 2、除了new Function(),其他的new出来的就是一个对象,对象没有prototype,a=function(){}; b=new a(); 那b也是一个对象
- 3、函数是对象的一种,函数继承于对象——func.prototype=Obect
- 4、在构造函数的proprotype上定义的属性和方法称为原型属性和原型方法,它的出现是为了让构造函数的实例可以共享原型属性和原型方法,那怎么让构造函数的实例找到这个方法呢,_proto__出现了。所以构造函数的prototype===构造函数的实例的_proto。再解释一下什么是构造函数,任何函数只有在被new调用之后才成为构造函数。比如 a=function(){},a 本来不是构造函数,但是如果有b = new a();那a就成了构造函数。
- 5、如果使用O=new Function();然后要寻找一个对象O的a属性即O.a,若O本身有a属性,则返回O.a,如果没有,就往Function.prototype也就是O._proto__上去找,找得到就返回o._proto.a,找不到就继续在O._proto.__proto__里找,如果还没有找到,那就继续在Function.prototype.__proto__也就是Object._ptoto__上找;还没有找到的话,就找不到了,因为Object._proto===null