寄生组合继承的核心目标是让父类构造函数只执行一次——仅在子类实例化时调用,避免原型链设置时的冗余执行,防止引用类型属性污染原型和内存浪费。

寄生组合继承的核心目标是让父类构造函数只执行一次——仅在子类实例化时调用,彻底避免在设置原型链时的冗余执行。这既节省开销,也防止父类引用类型属性(如数组、对象)被意外挂到子类原型上,造成数据污染或内存浪费。
为什么传统组合继承会调用两次?
问题出在原型链设置方式上:
- 第一次调用:出现在
Child.prototype = new Parent()这一步,父类构造函数被执行,this指向一个临时对象,该对象成为子类原型;结果是父类实例属性(如this.colors = ['red'])被写入Child.prototype,所有子类实例共享这份引用。 - 第二次调用:出现在子类构造函数中
Parent.call(this, name),为每个实例单独初始化属性;这次会覆盖原型上的同名属性,但第一次调用产生的副作用(比如原型上多出一个colors数组)已经发生且无法撤销。
寄生组合继承的关键操作
它用更干净的方式重建原型链,绕过 new Parent() 的副作用:
- 不直接用
new Parent()创建子类原型,而是用Object.create(Parent.prototype)创建一个空对象,其[[Prototype]]指向Parent.prototype,不执行任何构造逻辑。 - 手动修正新原型的
constructor属性,指向子类,避免丢失构造器标识。 - 子类构造函数中仍保留
Parent.call(this, ...),确保实例属性按需初始化。
标准封装写法(可直接复用)
把继承逻辑抽成工具函数,结构清晰、易于复用:
function inheritPrototype(Child, Parent) {
const prototype = Object.create(Parent.prototype);
prototype.constructor = Child;
Child.prototype = prototype;
}
function Parent(name) {
this.name = name;
this.tags = ['js', 'web'];
}
Parent.prototype.say = function() {
console.log(this.name);
};
function Child(name, age) {
Parent.call(this, name); // ✅ 只在这里调用一次
this.age = age;
}
inheritPrototype(Child, Parent); // ❌ 不再 new Parent()
Child.prototype.sayAge = function() {
console.log(this.age);
};
效果验证要点
运行后确认以下三点均已达成:
- 子类实例能正常访问
say()(原型链完整); - 修改
child1.tags.push('react')不会影响child2.tags(原型上无冗余实例属性); -
child1 instanceof Parent和child1 instanceof Child均为true(原型链和constructor正确)。











