现代javascript应避免深层继承,采用组合模式:抽离通用逻辑为独立模块,用依赖注入、props组合、mixin混入和工厂函数实现高内聚低耦合。

JavaScript 继承机制本身不鼓励深层继承链,因为原型链过深会带来耦合高、调试难、复用边界模糊等问题。真正现代、可维护的方案是:用组合模式替代“必须靠 extends 或 prototype 链传递能力”的思维,把行为拆成可插拔的模块,按需组装。
把共用逻辑抽成独立模块,不再靠继承传递
很多所谓“父类”其实只提供日志、校验、请求封装等通用能力,并不承载核心业务状态。这类逻辑不该塞进原型链里,而应做成纯函数或策略对象:
- 把表单校验规则抽象为 ValidationRule 实例,组件内部持有一个 rule 属性,运行时动态替换
- 将 API 调用封装为 ApiService 类,通过构造参数或 props 注入,而不是让每个子类都继承 BaseApiHandler
- 日志、埋点等横切关注点,用 useLogger 这类自定义 Hook(React)或工具函数调用,避免在构造函数里重复写 super.log()
用 props.children 和渲染属性实现 UI 层组合
在组件层面,“复用结构 + 自定义内容”完全不需要继承。React 官方早已明确反对组件继承,推荐以下方式:
DMap GL地图JSAPI(dmapgl)开发指南。用于编写、审查、调试 DMap 地图 API 代码,涵盖地图初始化、覆盖物渲染、图层管理、事件处理、控件交互等任务。当用户提及 dmap、dmapgl、jsapi‑gl 或相关地图开发需求时自动触发。
-
props.children:父组件预留内容插槽,如
<card><header></header></card>,Card 只管容器样式和生命周期,不干预子内容 -
渲染属性(render props):把变化逻辑交给回调,例如
<dataloader render="{data"><list items="{data}"></list>} /></dataloader> - Props 注入:把按钮样式、图标、加载状态等作为 prop 传入,而不是让 ButtonPrimary 继承 ButtonBase 再重写 render
用 Mixin 或 Object.assign 模拟能力注入,跳过原型链
当多个不相关类需要同一组方法(比如事件监听、拖拽、持久化),不要建 EventCapable → Draggable → Persistable 这样的继承树,而是直接混入:
- 定义 eventMixin = { on, emit, off },用
Object.assign(this, eventMixin)注入实例 - 在类定义中用
constructor() { super(); Object.assign(this, loggerMixin); }显式启用能力 - Vuex 的 mapState / mapActions、Redux 的 connect,本质都是这种“能力注入”,不是继承
用工厂或配置驱动初始化,消除构造函数层层 call
深层继承常伴随 Parent.call(this)、GrandParent.call(this) 这类脆弱调用。换成声明式创建更安全:
- 工厂函数接收配置对象:
createForm({ type: 'search', validation: 'strict', api: '/v1/search' }) - 内部根据配置组合 ValidationEngine、ApiAdapter、UIRenderer 等模块,返回干净实例
- 所有依赖显式传入,不隐含在 this 上下文或原型链中,便于单元测试和替换










