JavaScript继承(三)——组合继承中讲到,组合继承是JavaScript中最经常使用的继承模式,可是它也有本身的不足之处,如今咱们就来剖析它的不足,以下示例:javascript
function Human(name){ this.name = name; this.colors = ['white', 'yellow', 'black']; } Human.prototype.sayName = function(){ return this.name; } function Person(name, job){ //继承属性 Human.call(this, name); //定义本身的属性 this.job = job; } //继承方法 Person.prototype = new Human(); Person.prototype.constructor = Person; //定义本身的方法 Person.prototype.sayJob = function(){ return this.job; } let p = new Person('bob', 'JavaScript'); console.log(p.sayName());//bob
使用组合继承让Person
继承Human
实际上分为两步:java
Human.call(this, name)
继承属性。Person.prototype = new Human()
继承方法。可是在第二步中,当咱们为了继承方法而建立父类型的实例时,执行了一遍构造函数,将属性也初始化了,以下图所示:函数
然而并不须要这样作,咱们是想使用原型上的方法,属性的继承彻底能够留待new Person('bob', 'JavaScript')
时经过Human.call(this, name)
来调用父类型的构造函数,这里new Human()
等于事先多调用了一次Human
的构造函数,影响了代码的效率。this
再者,即使父类型已经初始化了属性,待未来建立子类型的对象,子类型对象的属性也会屏蔽父类型已经初始化好的属性,也就是说new Human()
初始化的属性根本就没什么用处,以下图所示: .net
p
上的colors
和name
会屏蔽掉原型上的colors
和name
, 那么有没有一种方案能够实现只使用原型上的方法,而不用调用父类型的构造函数呢?方案是有的,这就是本篇文章要讲的寄生组合式继承。prototype
咱们能够直观地想一下,既然只是想使用原型上的方法,不调用构造函数,那么直接将父类型的原型赋给子类型的原型不就能够了吗?像下面这样:3d
//继承方法 Person.prototype = Human.prototype;
表面上看是没什么问题,假如要用子类型对象调用sayName
方法时,对象上没有,因而到原型去找,原型就是Human
的原型,因而找到了sayName
方法,成功调用。可是结合原型链和对象深浅复制的知识(能够参考JavaScript继承(一)——原型链和JavaScript中对象的浅复制和深复制),Human
自己就是对象,Human.prototype
实际上是Human
原型对象的引用,Person.prototype = Human.prototype
就是引用的复制,结果就是Person.prototype
和Human.prototype
指向同一个对象——Human
的原型对象,而原型对象的constructor
属性是指向函数的,那么如今咱们让Human
原型对象的constructor
属性指向谁呢?Human
仍是Person
?显然原型链被打乱了。想起了哪吒的三头六臂,要是再有一个constructor
属性就行了。code
这时原型式继承又派上用场了,以下所示:对象
//继承方法 Person.prototype = object(Human.prototype); Person.prototype.constructor = Person;
咱们可使用原型式继承建立Human
原型对象的一个子对象,这个子对象赋给Person.prototype
,改变这个子对象的constructor
为Person
,这样既能够达到Person
的原型继承Human
的原型的效果,同时还没必要修改Human
原型的constructor
属性,也不须要调用Human
的构造函数,完美地解决了咱们的问题。blog
下面咱们来看下如今的原型链图:
咱们能够定义一个函数来实现这个逻辑:
function inheritPrototype(subType, superType){ subType.prototype = Object.create(superType.prototype); subType.prototype.constructor = subType; }
因而上面的组合继承能够改写为:
function Human(name){ this.name = name; this.colors = ['white', 'yellow', 'black']; } Human.prototype.sayName = function(){ return this.name; } function Person(name, job){ //继承属性 Human.call(this, name); //定义本身的属性 this.job = job; } //继承方法 inheritPrototype(Person, Human); //定义本身的方法 Person.prototype.sayJob = function(){ return this.job; } let p = new Person('bob', 'JavaScript'); console.log(p.sayName());//bob
继承方法时使用咱们本身封装的inheritPrototype
方法,这就是寄生组合式继承。
寄生组合式继承的高效率体如今它只调用了一次父类型的构造函数,而且所以避免了在子类型的原型上面建立没必要要的、多余的属性。与此同时,原型链还能保持不变;所以,还可以正常使用instanceof
和isPrototypeOf()
。开发人员广泛认为寄生组合式继承是引用类型最理想的继承模式。