js继承易引发耦合,应优先组合+接口;共享方法放原型但避免读写实例字段;多态逻辑抽为策略对象;模块按业务能力分层而非技术角色。

JS 继承机制本身不是高内聚低耦合的“解药”,反而是容易引发耦合的高危区。真正起作用的是设计意识:用继承前先问一句——它是否真的表达 is-a 关系?有没有更轻量、更可控的方式替代?
优先用组合 + 接口,而不是 extends
JavaScript 没有原生接口语法,但可以用函数签名、TypeScript interface 或约定来模拟。比如要复用“重试逻辑”:
- ❌ 避免让 ApiService extends RetryableService(强行建立父子关系,后续改 RetryableService 就可能破环 ApiService)
- ✅ 改为在 ApiService 内部持有一个 retryPolicy 实例:
private readonly retry = new ExponentialBackoffPolicy() - 调用时写
this.retry.execute(() => fetch(...)),行为可替换、可测试、不污染类职责
慎用原型链继承,明确隔离“共享逻辑”和“实例数据”
原型链适合挂载所有实例共用的方法(如 toString()),但不适合初始化状态或依赖外部环境:
- ❌ 不要在父类原型上定义
init()并期望子类自动调用;也不要把 API client、eventBus 等实例对象挂到 prototype 上 - ✅ 把需要独立状态的部分(如 loading 状态、缓存数据)放在构造函数中用
this.xxx = ...初始化 - ✅ 共享方法统一放原型,但确保它们不读写 this 上的业务字段(否则就是隐式耦合)
多态逻辑抽成策略对象,别堆在同一个类里
当看到 if/else 判断类型再调不同处理逻辑时,JS 的“继承”往往成了掩盖问题的遮羞布:
- ❌ 不要写
class PaymentProcessor { process(type) { if (type === 'alipay') { ... } else if (type === 'wechat') { ... } } - ✅ 定义策略接口:
const PaymentStrategy = { execute: (order) => Promise } - ✅ 各种支付方式作为独立对象:
const AlipayStrategy = { execute() { ... } } - ✅ Processor 只负责调度:
this.strategy.execute(order),注入谁就用谁,零耦合
包/模块层面按能力分层,不按技术角色切片
JS 项目常把所有 class 塞进 utils/ 或 helpers/,结果变成巧合内聚的垃圾场:
- ❌
src/utils/下混着DateFormatter.js、ApiErrorMapper.js、DomHelper.js—— 职责毫无关联 - ✅ 按业务能力组织:
src/order/(含 OrderForm、OrderValidator、OrderApiService),内部再分domain/、ui/、api/ - ✅ 跨模块通信走事件总线或显式回调,禁止
import { xxx } from '@/utils/dom'这类模糊依赖











