寄生组合继承:JavaScript 继承模式的优化方案

🤍 前端开发工程师、技术日更博主、已过CET6
🍨 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1
🕠 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》、《前端求职突破计划》
🍚 蓝桥云课签约作者、上架课程《Vue.js 和 Egg.js 开发企业级健康管理项目》、《带你从入门到实战全面掌握 uni-app》
文章目录
一、寄生组合继承的背景与目标
在 JavaScript 面向对象编程中,继承是实现代码复用和扩展的重要手段。之前提到的组合继承虽然融合了构造函数继承和原型链继承的优点,但存在一个明显的不足:父类构造函数会被调用两次。一次是在创建子类原型时(旧的实现方式),另一次是在子类构造函数中初始化子类实例时。寄生组合继承(Parasitic Combination Inheritance)正是为了解决这一问题而出现的,它在组合继承的基础上进行了优化,避免了不必要的父类构造函数调用,提高了性能。
二、寄生组合继承的实现步骤
寄生组合继承主要通过以下两个关键步骤来实现:
2.1 构造函数继承
和组合继承一样,在子类构造函数中使用 call 或 apply 方法调用父类构造函数,以此将父类的属性添加到子类实例中。这样能保证每个子类实例都有自己独立的属性副本,避免引用类型属性的共享问题。
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 中一种高效的继承模式,它在组合继承的基础上进行了优化,避免了父类构造函数的重复调用,同时保持了原型链的完整性和属性方法的有效分离。理解寄生组合继承的原理和实现方式,有助于开发者在不同场景下选择合适的继承模式,编写出高效、可维护的代码。
更多推荐


所有评论(0)