组合优于继承在javascript中体现为用对象拼装行为:通过持有能力实例、策略模式动态注入、装饰者模式叠加功能,实现高内聚低耦合;需避免伪组合,确保依赖可替换、无循环引用、生命周期一致。

组合优于继承不是一句空话,它在 JavaScript 中落地的关键是:用对象拼装行为,而不是靠原型链层层套娃。JS 本身基于原型,但过度依赖继承容易导致耦合高、复用难、调试乱——尤其当多个子类共享父类引用类型属性时,一个改,全跟着变。
明确“谁该拥有什么”:把能力拆成独立对象
继承常让人误以为“子类天然该有父类的一切”,而组合要求你主动思考:这个功能是不是必须属于这个类的“身份”?比如一个 Editor 类需要“保存”“撤销”“导出”能力,与其让 Editor 继承 Saveable、Undoable、Exportable 这三个类(根本不存在这种继承链),不如让 Editor 持有这些能力的实例:
- const editor = new Editor();
- editor.saver = new FileSaver();
- editor.undoManager = new UndoStack();
- editor.exporter = new PDFExporter();
这样每个能力可单独测试、替换、复用,Editor 本身职责清晰,不被父类包袱拖累。
用策略模式替代条件分支继承
遇到“根据不同类型执行不同逻辑”的场景,别急着写 Animal → Dog / Cat / Bird 多层继承。组合更轻量:
- 定义统一接口:class Renderer { render() { throw new Error('not implemented'); } }
- 实现具体策略:class SVGRenderer extends Renderer { render() { ... } },class CanvasRenderer extends Renderer { render() { ... } }
- 运行时注入:chart.setRenderer(new SVGRenderer()); 或 chart.setRenderer(new CanvasRenderer());
新增渲染方式不用动 Chart 类,也不用改继承结构,只加一个新类再传进去就行。
装饰者模式动态叠加功能
想给对象临时增强能力?比如给一个普通 Button 加上“防抖”“权限校验”“埋点上报”,继承会逼你造出 ButtonWithDebounce、ButtonWithAuth、ButtonWithTracking 等一堆子类。组合用装饰者更自然:
- const button = new Button();
- const debounced = new DebounceDecorator(button);
- const secured = new AuthDecorator(debounced);
- secured.click(); // 链式触发所有装饰逻辑
每层装饰器只关注一件事,顺序可调、可增可删,完全避开继承树膨胀问题。
警惕“伪组合”:别把 new 写满构造函数
组合不是把所有依赖都塞进 constructor 就算完事。要注意三点:
- 依赖应通过参数传入,而非在类内部 new —— 方便 mock 和替换;
- 避免循环依赖:A 组合 B,B 又组合 A,会导致初始化失败;
- 组合对象生命周期要对齐:比如一个 Service 组合了 Logger,Logger 不该在 Service 销毁后还被调用。
真正松耦合的组合,让每个模块像乐高积木一样可插拔,而不是浇铸在一起的混凝土块。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











