组合继承中父类构造函数被调用两次:第一次在子类构造函数中通过Parent.call(this)初始化实例属性,确保属性私有化和传参;第二次在Child.prototype = new Parent()时创建原型对象,虽实现方法复用却导致冗余执行和副作用。

组合继承中父类构造函数被调用两次,是因为它把两种继承方式“拼”在了一起:一次靠 构造函数调用(用于继承实例属性),另一次靠 原型链设置(用于继承原型方法)。这两步各自触发了一次父类构造函数执行,且目的完全不同。
第一次调用:在子类构造函数里显式 call
这是有意为之,目的是让子类实例拥有自己独立的属性副本:
- 比如父类中有
this.name = name、this.tags = ['js', 'web']这类实例属性 - 子类构造函数中写
Parent.call(this, name),就把这些属性直接挂到当前子类实例上 - 每个子类实例都有自己的
tags数组,互不干扰
第二次调用:在设置子类原型时 new Parent()
这一步是为了让子类能访问父类原型上的方法(如 say()),但实现方式带来了副作用:
- 写
Child.prototype = new Parent(),本质是执行了一次new Parent() - 哪怕只传空参或默认值,父类构造函数仍会运行,生成一个临时父类实例
- 这个临时实例成了子类原型,它的
name、tags等属性也被创建了——但完全没被子类实例使用 - 更麻烦的是,如果父类构造函数有副作用(如发请求、改全局状态),这里就白执行了一次
为什么不能只保留其中一次?
只保留第一次(call)→ 子类实例有属性,但没有父类方法(say 调不到);
只保留第二次(new Parent())→ 子类原型有方法,但实例没属性,也无法传参初始化。
立即学习“Java免费学习笔记(深入)”;
所以组合继承本意是取长补短,只是“复用父类原型”这件事,不该靠执行构造函数来实现——寄生组合式继承正是用 Object.create(Parent.prototype) 替代 new Parent(),绕开了第二次调用。


















