可封装通用继承工具函数实现组合继承:先用call继承实例属性并传参,再用空函数中介继承原型方法,修正constructor,避免污染父类原型。

可以封装一个通用的继承工具函数,核心目标是:让子构造函数安全、干净地继承父构造函数的实例属性(通过 call)和原型方法(通过 prototype 链接),同时修复 constructor 指向,并避免污染父类原型。下面是一个生产可用的简洁实现:
1. 基础版工具函数(组合继承封装)
该函数实现「构造函数继承 + 原型链继承」的标准组合模式,解决引用共享、参数传递、constructor 修正三大问题:
function inherit(Child, Parent) {
// 步骤1:继承父类实例属性(支持传参)
Child.prototype._super = function(...args) {
Parent.apply(this, args);
};
// 步骤2:继承父类原型方法(不执行 new Parent(),避免副作用)
const F = function() {};
F.prototype = Parent.prototype;
Child.prototype = new F();
// 步骤3:修正 constructor
Child.prototype.constructor = Child;
// 步骤4:提供便捷的 super 调用(可选增强)
Child.prototype.super = function(methodName, ...args) {
if (Parent.prototype[methodName]) {
return Parent.prototype[methodName].apply(this, args);
}
};
}
使用示例:
function Animal(name) {
this.name = name;
this.hobbies = ['sleep'];
}
Animal.prototype.say = function() {
console.log(`${this.name} says hello`);
};
function Dog(name, breed) {
this._super(name); // 继承实例属性
this.breed = breed;
}
Dog.prototype.bark = function() {
console.log(`${this.name} barks!`);
};
inherit(Dog, Animal); // 一行完成继承配置
const d = new Dog('Xiaohei', 'Shepherd');
d.say(); // ✅ 正常调用父类原型方法
d.bark(); // ✅ 子类自有方法
console.log(d.hobbies); // ✅ 独立副本,不共享
console.log(d.constructor === Dog); // ✅ true
2. 支持多级继承与原型方法覆盖检测
若需更健壮的工具(如防止意外重写父方法、兼容 ES5 环境、支持多次 inherit 调用),可扩展为:
- 自动跳过不可枚举/原生方法(如 toString、hasOwnProperty)
- 在 Child.prototype 上添加 __protoOf__ 属性记录父原型,便于调试
- 提供 hasOwnMethod 辅助判断方法是否定义在子类自身(而非继承而来)
3. 现代替代方案提醒
注意:如果你的运行环境支持 ES6+(绝大多数场景已满足),直接用 class + extends 是最推荐的方式:
class Animal {
constructor(name) {
this.name = name;
this.hobbies = ['sleep'];
}
say() { console.log(`${this.name} says hello`); }
}
class Dog extends Animal {
constructor(name, breed) {
super(name); // ✅ 自动处理实例继承
this.breed = breed;
}
bark() { console.log(`${this.name} barks!`); }
}
它底层已封装好组合继承逻辑,语义清晰、调试友好、被所有主流引擎深度优化。
4. 不建议直接用的“简陋版”陷阱
以下写法看似省事,但存在严重隐患,应避免:
-
Child.prototype = Object.create(Parent.prototype)—— 缺少 constructor 修正,且未处理实例属性继承 -
Child.prototype = new Parent()—— 父类构造函数被无参执行,可能引发副作用(如发请求、改全局状态) - 仅用
Parent.call(this)—— 无法复用原型方法,丧失继承意义











