javascript 高级程序设计 8.3.6 寄生式组合继承

47 阅读2分钟

寄生式组合继承

由于组合继承中,父类的构造函数会被调用两次,会造成子类的原型里出现了不需要的父类构造函数中的属性.而子类的实例中也会由于组合式继承中采用盗用构造函数,而拥有自己的属性.

究其根源,就是在子类的原型在被赋值为父类实例的时候,不应该调用父类的构造函数而拥有父类的属性,子类的原型只需要有父类原型上的东西,不需要父类构造函数里的东西.

// 定义父类构造函数
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()方法去确定关系.是引用继承中的最佳模式.