寄生组合继承的核心作用是让父类构造函数仅在子类实例化时执行一次,避免传统组合继承中因child.prototype = new parent()导致的原型上冗余初始化及数据共享问题。

寄生组合继承的核心作用,就是让父类构造函数只执行一次——仅在子类实例化时调用,彻底绕开原型设置阶段的冗余执行。
为什么二次调用会出问题
传统组合继承中,父类构造函数会在两个地方被触发:
-
第一次:出现在
Child.prototype = new Parent()这一步。此时 this 指向一个临时对象,该对象成为子类原型。结果是父类的实例属性(比如this.colors = ['red'])被挂到Child.prototype上,所有子类实例共享这份引用; -
第二次:出现在子类构造函数里的
Parent.call(this, name)。这次为每个实例单独初始化,会覆盖原型上的同名属性,但第一次产生的副作用(如原型上多出一个数组)已无法撤销。
这不仅浪费执行资源,还可能引发数据污染、计数器错乱、重复发起请求等实际问题。
关键操作:切断原型设置时的构造调用
寄生组合继承不依赖 new Parent() 来设原型,而是用更轻量、更干净的方式建立原型链:
- 用
Object.create(Parent.prototype)创建一个空对象,它的[[Prototype]]直接指向父类原型,不执行父类构造函数; - 手动把新对象的
constructor指回子类,避免后续instanceof或obj.constructor出错; - 子类构造函数内仍保留
Parent.call(this, ...),确保每个实例拥有独立的属性副本。
标准封装写法(可直接复用)
把继承逻辑抽成工具函数,避免每次重复写原型重设:
function inherit(Child, Parent) {
const proto = Object.create(Parent.prototype);
proto.constructor = Child;
Child.prototype = proto;
}
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;
}
inherit(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 正确)。
不复杂但容易忽略











