组合继承中父类构造函数被调用两次:第一次在子类实例化时通过Parent.call(this)合理初始化实例属性;第二次在Child.prototype = new Parent()时冗余执行,造成内存浪费、性能开销及副作用风险;寄生组合式继承通过Object.create(Parent.prototype)避免该冗余。

组合继承中调用两次父类构造函数,不是语法错误,而是设计逻辑带来的**冗余执行**——它确实发生,且有明确的性能影响,但关键在于理解“哪里多了一次”以及“为什么这次是多余的”。
第一次调用:在子类实例化时(合理且必要)
子类构造函数内部通过 Parent.call(this, ...) 调用父类构造函数,目的是把父类定义的实例属性(如 this.name、this.friends = [])正确挂载到当前子类实例上。这是必须的,确保每个子类实例拥有独立的属性副本,避免引用类型共享问题。
第二次调用:在设置原型链时(冗余且可避免)
紧接着,代码会执行:
Child.prototype = new Parent();
这句看似是为了让子类能访问父类原型上的方法,但它实际触发了完整的父类构造函数执行——此时创建的是一个临时的父类实例,仅用于充当子类原型对象。这个实例不会被任何子类对象直接使用,它的属性(比如又一个 this.friends = [])完全浪费,且如果父类构造函数中有副作用(如日志、计数、DOM操作、异步初始化等),这些都会被不必要地执行一次。
两次调用带来的具体损耗
- 内存占用增加:多创建一个父类实例对象,其所有实例属性(包括可能很大的数组或嵌套对象)都被分配内存,却永不被访问;
- 执行开销重复:父类构造函数体内的逻辑(赋值、条件判断、循环等)运行两遍,对高频创建的类(如渲染组件、数据模型)会累积可观耗时;
- 潜在副作用风险:若父类构造函数依赖外部状态(如全局计数器、缓存标记、事件绑定),第二次调用可能引发意料之外的行为,调试困难。
寄生组合式继承如何解决
它用 Object.create(Parent.prototype) 替代 new Parent() 来设置子类原型。这样做的本质是:
– 不执行父类构造函数;
– 只建立原型链连接(即子类原型的 __proto__ 指向 Parent.prototype);
– 父类原型上的方法依然可被子类实例继承,而父类构造函数只在子类实例化时被调用一次。
不复杂但容易忽略



















