寄生组合式继承是ES5最推荐的继承方式,它通过空函数中转设置子类原型,使父类构造函数仅执行一次,既避免冗余属性和副作用,又保持属性独立性与方法复用性。

寄生组合式继承是目前 ES5 环境下最推荐的继承实现方式,它本质上是对“组合继承”的优化,核心目标是只调用一次父类构造函数,同时保留属性独立性与方法复用性。
为什么组合继承有缺陷
组合继承 = 原型链继承(复用方法) + 构造函数继承(初始化实例属性)。但它存在一个关键问题:
- 父类构造函数被调用了两次:一次在
SubType.prototype = new SuperType()中,另一次在子类构造函数内通过SuperType.call(this, ...)调用 - 第一次调用会在子类原型上留下一份冗余的父类实例属性(如
this.name、this.colors),虽然后续子类实例的同名属性会遮蔽它,但内存中仍存在,且可能引发逻辑混淆 - 若父类构造函数有副作用(如发请求、改全局状态),会被意外触发两次
寄生组合式继承怎么做
它的思路很清晰:用一个“干净的中间对象”代替 new SuperType() 来设置子类原型,从而跳过父类构造函数的首次执行。
- 先定义一个空函数
F,将其prototype指向父类原型 - 再用
new F()创建一个不执行父类构造逻辑、但能正确链接到父类原型链的对象 - 把这个对象赋给子类的
prototype,再手动修正constructor
典型实现如下:
function inheritPrototype(SubType, SuperType) {
const F = function() {};
F.prototype = SuperType.prototype;
SubType.prototype = new F();
SubType.prototype.constructor = SubType;
}
function SuperType(name) {
this.name = name;
}
SuperType.prototype.sayName = function() {
console.log(this.name);
};
function SubType(name, age) {
SuperType.call(this, name); // 只在这里调用一次
this.age = age;
}
inheritPrototype(SubType, SuperType);
SubType.prototype.sayAge = function() {
console.log(this.age);
};
关键细节不能漏
这个模式真正起作用,依赖几个不可省略的环节:
-
必须用空函数做中转:直接写
SubType.prototype = Object.create(SuperType.prototype)在旧环境可能不兼容,而new F()是更通用的 polyfill 方式 -
constructor 必须手动修复:否则
SubType.prototype.constructor === SuperType,影响 instanceof 判断和调试可读性 - 父类方法只能定义在 prototype 上:寄生式继承只接管原型链,不处理构造函数内定义的方法(那些本就该由 call 继承)
它解决了什么,没解决什么
寄生组合式继承明确聚焦于“构造函数调用次数”和“原型链完整性”两个痛点:
- ✅ 避免父类构造函数重复执行,消除副作用与内存冗余
- ✅ 子类实例拥有独立属性(靠 call),方法可复用(靠原型链)
- ✅ 支持向父类传参、保持 instanceof 正确性、可扩展原型方法
- ❌ 不解决多继承问题(JS 天然不支持)
- ❌ 不改变 JavaScript 原型本质,instanceof 和 isPrototypeOf 依然依赖原型链走向











