JavaScript通过原型链混入实现多重继承效果,常用Object.assign直接拷贝可枚举方法到prototype,需修复constructor;高阶函数式Mixin返回新类,天然避免constructor问题;精确混入需用getOwnPropertyDescriptors+defineProperties保留getter/setter等元信息。

JavaScript 本身不支持多重继承,但可以通过原型链混入多个对象的方法,把多个“功能包”挂载到目标类的 prototype 上,实现行为叠加。关键不是让子类继承多个父类,而是把多个对象的方法**复制或委托**到一个原型上。
用 Object.assign 直接混入到 prototype
这是最常用、最直观的方式:把多个源对象的可枚举方法一次性拷贝到目标类的原型上。
- 确保只混入方法(不混入构造逻辑或实例属性),避免污染实例状态
- 混入顺序决定覆盖关系:后混入的对象中同名方法会覆盖前面的
- 混入后记得手动修复
constructor,否则MyClass.prototype.constructor会指向Object
示例:
class Button {}const Clickable = { click() { console.log('clicked'); } };
const Draggable = { drag() { console.log('dragged'); } };
Object.assign(Button.prototype, Clickable, Draggable);
Button.prototype.constructor = Button; // 修复 constructor
const btn = new Button();
btn.click(); // ✅
btn.drag(); // ✅
用高阶函数封装 Mixin,支持链式调用
把混入逻辑封装成接收类、返回新类的函数,更安全、更易复用,也方便组合多个 Mixin。
立即学习“Java免费学习笔记(深入)”;
- 每个 Mixin 函数只负责扩展
prototype,不修改原类定义 - 可按需叠加,比如
class X extends MixinA(MixinB(Base)) {} - 天然规避
constructor被覆盖问题(因为是返回新类)
示例:
const Loggable = (Base) => class extends Base {log(msg) { console.log(`[${Date.now()}] ${msg}`); }
};
const Serializable = (Base) => class extends Base {
toJSON() { return JSON.stringify(this); }
};
class User {
constructor(name) { this.name = name; }
}
class EnhancedUser extends Loggable(Serializable(User)) {}
const u = new EnhancedUser('Alice');
u.log('created'); // ✅
u.toJSON(); // ✅
手动遍历 descriptor 混入,保留 getter/setter 和不可枚举属性
Object.assign 只拷贝可枚举自有属性,若 Mixin 中包含 get/set、configurable: false 或不可枚举方法,就得用 Object.getOwnPropertyDescriptors + Object.defineProperties。
- 适合对方法元信息有要求的场景(如装饰器、响应式系统)
- 能精确控制每个属性的
writable、enumerable等特性 - 需要递归处理嵌套对象时,要额外判断是否为普通对象
示例:
const Observable = {get state() { return this._state || {}; },
set state(v) { this._state = v; this.notify(); },
notify() { console.log('notified'); }
};
// 完整混入(含 getter/setter)
Object.defineProperties(
MyClass.prototype,
Object.getOwnPropertyDescriptors(Observable)
);
注意原型链查找与方法冲突
混入后所有方法都在同一层 prototype 上,没有“继承优先级”,只有定义顺序和属性屏蔽规则。
- 同名方法会被后混入的覆盖,无法像
super那样显式调用前一个 - 如果需要保留原始逻辑,Mixin 内部应提供钩子(如
beforeClick)或使用组合模式替代 - 可通过
hasOwnProperty或Object.prototype.propertyIsEnumerable判断方法来源,调试时有用


















