在这里插入图片描述

🤍 前端开发工程师、技术日更博主、已过CET6
🍨 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1
🕠 牛客高级专题作者、打造专栏《前端面试必备》《2024面试高频手撕题》《前端求职突破计划》
🍚 蓝桥云课签约作者、上架课程《Vue.js 和 Egg.js 开发企业级健康管理项目》《带你从入门到实战全面掌握 uni-app》

一、寄生组合继承的背景与目标

在 JavaScript 面向对象编程中,继承是实现代码复用和扩展的重要手段。之前提到的组合继承虽然融合了构造函数继承和原型链继承的优点,但存在一个明显的不足:父类构造函数会被调用两次。一次是在创建子类原型时(旧的实现方式),另一次是在子类构造函数中初始化子类实例时。寄生组合继承(Parasitic Combination Inheritance)正是为了解决这一问题而出现的,它在组合继承的基础上进行了优化,避免了不必要的父类构造函数调用,提高了性能。

二、寄生组合继承的实现步骤

寄生组合继承主要通过以下两个关键步骤来实现:

2.1 构造函数继承

和组合继承一样,在子类构造函数中使用 callapply 方法调用父类构造函数,以此将父类的属性添加到子类实例中。这样能保证每个子类实例都有自己独立的属性副本,避免引用类型属性的共享问题。

function Parent(name) {
    this.name = name;
    this.colors = ['red', 'blue', 'green'];
}
Parent.prototype.sayName = function () {
    console.log(this.name);
};

function Child(name, age) {
    Parent.call(this, name); // 调用父类构造函数,将父类属性添加到子类实例
    this.age = age;
}

2.2 寄生式继承原型

这里不会像组合继承那样使用 new Parent() 来创建子类原型,而是通过 Object.create() 方法创建一个以父类原型为原型的新对象,再将这个新对象赋值给子类的原型。之后,修正子类原型的 constructor 属性,使其指向子类本身。

function inheritPrototype(Child, Parent) {
    var prototype = Object.create(Parent.prototype); // 创建一个以父类原型为原型的新对象
    prototype.constructor = Child; // 修正 constructor 属性
    Child.prototype = prototype; // 将新对象赋值给子类原型
}

inheritPrototype(Child, Parent);

Child.prototype.sayAge = function () {
    console.log(this.age);
};

三、完整代码示例

function Parent(name) {
    this.name = name;
    this.colors = ['red', 'blue', 'green'];
}
Parent.prototype.sayName = function () {
    console.log(this.name);
};

function Child(name, age) {
    Parent.call(this, name); // 构造函数继承,初始化父类属性
    this.age = age;
}

function inheritPrototype(Child, Parent) {
    var prototype = Object.create(Parent.prototype);
    prototype.constructor = Child;
    Child.prototype = prototype;
}

inheritPrototype(Child, Parent);

Child.prototype.sayAge = function () {
    console.log(this.age);
};

var child1 = new Child('John', 20);
child1.colors.push('yellow');
console.log(child1.colors); // 输出: ['red', 'blue', 'green', 'yellow']
child1.sayName(); // 输出: John
child1.sayAge(); // 输出: 20

var child2 = new Child('Jane', 25);
console.log(child2.colors); // 输出: ['red', 'blue', 'green']
child2.sayName(); // 输出: Jane
child2.sayAge(); // 输出: 25

四、寄生组合继承的优势

4.1 避免重复调用父类构造函数

在组合继承里,创建子类原型时会调用一次父类构造函数,在子类构造函数中又会调用一次。而寄生组合继承仅在子类构造函数中调用一次父类构造函数,避免了不必要的调用,减少了性能开销。

4.2 保持原型链的完整性

通过 Object.create() 方法创建的新对象,能确保子类原型和父类原型之间的正确继承关系,同时避免了因创建父类实例作为子类原型而可能带来的问题。

4.3 实现属性和方法的有效分离

寄生组合继承结合了构造函数继承和原型链继承的优点,既保证了子类实例拥有独立的属性,又实现了方法的共享和复用。

五、与其他继承模式的对比

5.1 与原型链继承对比

原型链继承存在引用类型属性共享的问题,且无法在子类实例化时向父类构造函数传递参数。而寄生组合继承通过构造函数继承解决了这些问题,使得每个子类实例都有自己独立的属性。

5.2 与组合继承对比

组合继承虽然解决了原型链继承的问题,但会重复调用父类构造函数。寄生组合继承则避免了这一问题,提高了性能。

5.3 与 ES6 类继承对比

ES6 的类继承语法更加简洁直观,不过其底层原理和寄生组合继承相似。在不支持 ES6 的环境中,寄生组合继承是一种很好的替代方案。

六、总结

寄生组合继承是 JavaScript 中一种高效的继承模式,它在组合继承的基础上进行了优化,避免了父类构造函数的重复调用,同时保持了原型链的完整性和属性方法的有效分离。理解寄生组合继承的原理和实现方式,有助于开发者在不同场景下选择合适的继承模式,编写出高效、可维护的代码。

更多推荐