组合优于继承是解决JavaScript类继承僵化、耦合高、复用难的策略:用函数/对象封装行为(如validateEmail、Logger实例)、属性委托(如Order持paymentService)、运行时组装(如withAuth(withValidation(createEntity()))),避免抽象基类,以“有”替代“是”。

组合优于继承不是一句空话,而是解决 JavaScript 中类继承僵化、耦合高、难以复用等问题的实际策略。核心思路是:**用对象拼装行为,而不是靠父类“传代”来获取功能**。
用函数或对象封装可复用行为
把独立能力(如日志、验证、缓存)抽成纯函数或配置对象,需要时直接调用或挂载,不依赖 class 层级关系。
- 比如验证逻辑,写成
validateEmail(value)或createValidator({ type: 'email' }),任何对象都能用,无需让 User 继承 ValidatorBase - 日志功能可封装为
Logger实例,通过属性注入:user.logger = new Logger({ level: 'debug' })
用属性委托替代继承链
让一个对象持有另一个对象的实例,并把相关操作委托给它,而不是 extends 一个带一堆无关方法的父类。
- 例如订单(
Order)需要支付能力,不要继承PaymentHandler,而是:this.paymentService = new StripeService(),再定义pay() { return this.paymentService.charge(...) } - 这样 Order 只暴露自己关心的接口,且随时可替换
paymentService为 PayPalService,不影响其他逻辑
用工厂或构造函数组装对象
把“创建一个具备多种能力的对象”这件事,从 class 定义转移到运行时组合。
- 定义基础构造器:
function createEntity(data) { return { ...data } } - 再用增强函数叠加能力:
const user = withAuth(withValidation(createEntity({ name: 'Alice' }))) - 每个
withXxx返回新对象(或原对象扩展),职责单一、可测试、可复用
避免“继承式插件”陷阱
别为了“复用”而设计一堆抽象基类(如 BaseComponent、EventEmitterMixin),再让子类去继承或混入——这本质还是继承思维。
- 事件能力直接用
new EventEmitter()实例,挂到对象上:obj.emitter = new EventEmitter() - 组件状态管理交给外部 store(如 Zustand)或自定义 hook(React 中),而非让组件继承
StatefulComponent - 真正需要“多态”的地方,用鸭子类型 + 接口约定(比如所有能
.save()的对象都实现该方法),不强求 class 关系
组合不是拒绝 class,而是把 class 当作结构容器,把逻辑拆出去、配进来。代码更易测、更易换、更贴近真实协作场景——你不会让一个用户“是”一个日志器,但你可以让他“有”一个日志器。











