继承是面向对象语言中一个最为人津津乐道的概念。许多面向对象语言都支持两种继承方式:接口继承和实现继承。接口继承只继承方法签名,而实现继承则继承实际的方法。在ECMAScript中,函数没有签名,没法实现接口继承,只支持实现继承,继承主要依靠的是原型链。函数
先来看一个简单的原型链继承示例,以下图:this
首先声明一个Human
函数,给它的原型添加一个getHumanSex
方法。而后声明一个Person
函数,要让Person
继承Human
,能使用Human
的属性和方法,只要把Person
的原型从新赋值为Human
的一个实例就行了。最后给Person
的原型(也就是Human
的这个实例)添加一个getPersonSex
方法。如今建立Person
的一个实例personObj
,name = '张三',age = 12
。若是访问personObj.name
,那么就能够直接访问到是张三
了。假如如今要访问humanSex
,由于personOb
j中没有,因此会顺着__proto__
属性到Person
的原型中去找,而Person
的原型就是Human
的一个实例,它的humanSex
属性就是'男/女'
,因此personObj.humanSex
的值就是'男/女'
,这就达到了继承的效果。假如要访问getPersonSex
,personObj
中没有,也是到原型中去找,找到了就调用,由于是使用personObj.getPersonSex
,因此在方法getPersonSex
中,this
指的就是personObj
,return this.personSex
就是返回personObj.personSex
,即为'男'
。 若是是调用personObj.getHumanSex
,仍是先顺着__proto__
到Person
的原型中去找,Person
的原型是Human
的一个实例,里面没有,那怎么办呢,由于该原型是Human
的一个实例,因此它天然也会有个__proto__
指向Human
的原型,因而就继续顺着这个__proto__
到Human
的原型中去找,这就是所谓的原型链,找到了就开始调用,天然getHumanSex
中的this
指的也就是personObj
,return this.humanSex
就是return personObj.humanSex
,即'男/女'
。这就是原型链继承。.net
细心的读者也许会发现,在Human
的原型中也有个__proto__
,那么Human
的原型会不会也是某个函数的一个实例呢?若是咱们调用personObj.toString()
,那么咱们调用的到底是谁的toString()
呢?要回答这个问题,咱们来看下面这幅示意图:code
Human
的原型实际上是Object
的一个实例,天然__proto__
指向的就是Object
的原型,而toString
就是Object
原型上的方法。对象
其实ECMAScript中全部引用类型的默认原型都是Object
的实例,也就是说全部引用类型默认都继承了Object
,而这个继承正是经过原型链实现的。blog
原型的好处是全部的实例能共享它所包含的属性和方法,也就是说没必要在构造函数中定义对象实例的信息,但正如JavaScript建立对象(三)——原型模式中所说,这也带来了一个问题。原型属性会被全部实例共享,对于引用类型会出现一些应用时的共享问题,因此须要在构造函中,而不是在原型对象中定义属性。可是在经过原型来实现继承时,原型实际上就是另外一个函数的实例。因而,原来的实例属性也就瓜熟蒂落地变成了如今的原型属性了,那么原型共享数据的问题又出现了。继承
另一个问题是,建立子类型的实例时,无法向超类型的构造函数中传递参数。实际上,应该说是没办法在不影响全部对象的实例下,给超类型的构造函数传递参数。有鉴于此,实践中不多会单独使用原型链。接口