寄生式组合继承
由于组合继承中,父类的构造函数会被调用两次,会造成子类的原型里出现了不需要的父类构造函数中的属性.而子类的实例中也会由于组合式继承中采用盗用构造函数,而拥有自己的属性.
究其根源,就是在子类的原型在被赋值为父类实例的时候,不应该调用父类的构造函数而拥有父类的属性,子类的原型只需要有父类原型上的东西,不需要父类构造函数里的东西.
// 定义父类构造函数
function father(name) {
this.name = name
this.color = ['red','green']
}
father.prototype.sayName = function() {
console.log(this.name)
}
// 定义子类构造函数
function son(name,age) {
father.call(this,name) // 在这里调用了父类构造函数,这里是需要的
this.age = age
}
// 子类继承父类原型上的方法
son.prototype = new father() // 在这里也调用了父类构造函数,但是在原型接受了父类构造函数属性,不好
// 子类定义自己的方法
son.prototype.sayAge = function() {
console.log(this.age)
}
// 产生两个实例
let son1 = new son('张三',20)
son1.color.push('blue')
console.log(son1.color) // ['red','green','blue']
son1.getName() // '张三'
son1.getAge() // 20
就像上面的例子,
father.call(this,name)
子类函数son的构造函数里的call,调用了构造函数,没有毛病,确保子类的实例各自有各自的属性.
son.prototype = new father()
子类的原型被赋值了父类的name和color,这实在是没必要
所以我们需要寄生式组合继承来解决这个问题
思路是: 不通过new父类构造函数给子类原型赋值,而是只取父类原型的一个副本.
// 定义一个方法,提取父类的原型然后给到子类的原型
// 方法接收父类构造函数和子类的构造函数
function extendPrototype(father,son) {
let prototype = Object.create(father.prototype) // 抽取父类原型
prototype.constructor = son // 改变抽取出来的原型里构造函数的指回,原本指回father,改为son
son.prototype = prototype // 把抽取的原型赋值给子类原型
}
通过调用这个函数,就可以把父类的原型副本赋值给子类原型,避免了出现调用构造函数的情况.
我们的例子就可以改成这样:
// 定义父类构造函数
function father(name) {
this.name = name
this.color = ['red','green']
}
father.prototype.sayName = function() {
console.log(this.name)
}
// 定义子类构造函数
function son(name,age) {
father.call(this,name) // 在这里调用了父类构造函数,这里是需要的
this.age = age
}
// 直接通过这个方法把父类的原型移植给子类原型,还不修改其构造函数
extendsPrototype(father,son)
// 子类定义自己的方法
son.prototype.sayAge = function() {
console.log(this.age)
}
// 产生两个实例
let son1 = new son('张三',20)
son1.color.push('blue')
console.log(son1.color) // ['red','green','blue']
son1.getName() // '张三'
son1.getAge() // 20
寄生式组合继承,避免了组合继承里的的问题,也没有摧毁原型之间的关系,可以使用instanceof操作符和isPrototypeOf()方法去确定关系.是引用继承中的最佳模式.