寄生组合式继承的核心目标是让父类构造函数仅在子类实例化时执行一次,避免在设置原型时冗余调用;它通过Object.create(Parent.prototype)创建干净原型、手动修正constructor,并在子类构造函数中调用Parent.call(this)实现解耦。

寄生组合式继承的核心目标,就是只让父类构造函数执行一次——仅在子类实例化时调用,彻底避开在设置子类原型时的冗余调用。这既节省了执行开销,也防止了父类实例属性(尤其是引用类型)意外挂载到子类原型上,造成数据污染或内存浪费。
为什么二次调用会出问题
在传统组合继承中,这两处调用不可分割:
-
第一次调用:出现在
Child.prototype = new Parent()这一步。此时父类构造函数被执行,this指向一个临时对象,该对象成为子类原型。结果是:Parent的实例属性(如this.colors = ['red'])被写入Child.prototype,所有子类实例共享这份引用。 -
第二次调用:出现在
Parent.call(this, name)中,为每个子类实例单独初始化自己的属性。这次写入会覆盖原型上的同名属性,但第一次调用产生的副作用(比如原型上多出一个colors数组)已经发生,且无法撤销。
寄生组合式继承的关键操作
它把“继承父类方法”和“继承父类实例属性”完全解耦,用更轻量的方式复用原型链:
- 不再用
new Parent()去创建子类原型,而是用Object.create(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 正确)。










