JavaScript通过运行时动态分发与统一接口约定实现多态,包括继承式抽象方法、鸭子类型、工厂模式及参数判别模拟重载,核心是接口一致与行为可预期。

JavaScript 的 class 本身不提供编译期多态(如 Java 那样的方法重载),但通过**运行时动态分发 + 统一接口约定**,完全可以实现实用、灵活的多态性,尤其适合编写可复用的通用组件。
统一方法名 + 子类按需实现
这是最直接也最常用的多态写法。父类定义“契约”(即公共方法名),子类各自实现具体逻辑,调用方只认方法名,不关心具体类型。
- 父类声明抽象行为(哪怕只是空方法或 throw 提示)
- 每个子类覆盖该方法,提供符合自身语义的实现
- 组件接收任意满足该接口的对象,运行时自动调用对应版本
例如一个通用表格渲染器:
<tr>
<td>${this.user.name}</td>
<td>${this.user.email}</td>
</tr>
利用鸭子类型避免强继承依赖
不必非要让所有类都继承同一个基类。只要对象有某个方法,就能被通用逻辑消费——这就是 JavaScript 天然支持的“鸭子类型”,是更轻量、更解耦的多态实践。
- 组件函数只检查目标对象是否具备必要方法(
typeof obj.draw === 'function') - 传入的对象可以来自任意构造方式:class 实例、字面量对象、工厂函数返回值
- 没有继承树负担,适合插件化、配置驱动的组件设计
比如一个图表绘制组件:
<canvas width="400" height="200"></canvas>
配合 static 方法与 factory 模式提升灵活性
当组件需要根据参数动态选择行为时,可在 class 上定义 static create() 或使用工厂函数,把“创建哪个子类”的逻辑封装起来,对外保持统一接口。
- 用户仍只和一个名字交互(如
Button.create(type)) - 内部根据 type 返回不同子类实例,各子类实现自己的
render()、handleClick() - 扩展新类型只需新增子类 + 修改 factory,不改动已有调用代码
示例:
<button class="btn-primary">${this.text}</button>
注意边界:JS 没有重载,但可用参数判别模拟
严格来说,JS 不支持基于参数签名的重载(同名函数多个定义)。但可通过参数类型/数量做运行时分支,达到类似效果,适合配置型组件。
- 在方法内用
arguments.length、typeof、Array.isArray()等判断 - 适用于构造函数或初始化方法,让同一组件支持多种初始化方式
- 比写多个方法名更简洁,但需文档明确说明行为差异
例如:
class Modal {
constructor(options) {
if (typeof options === 'string') {
// 字符串:当作内容文本
this.content = options;
this.title = 'Info';
} else if (options && typeof options === 'object') {
// 对象:完整配置
this.title = options.title || 'Modal';
this.content = options.content;
this.onClose = options.onClose;
}
}
}
不复杂但容易忽略:多态的价值不在语法炫技,而在于让组件使用者只关注“做什么”,不用操心“怎么做”。只要守住接口一致、行为可预期这两条线,class 语法就能支撑起健壮又易扩展的通用组件体系。











