JavaScript可通过构造函数抛错、静态属性声明契约及JSDoc提示模拟接口和抽象类:基类constructor检查子类是否实现必需方法,static requiredMethods显式列出方法,JSDoc配合TS插件提供类型提示。

JavaScript 本身没有原生的 interface 或 abstract class 关键字(ES2022 之前),但可以通过约定 + 运行时检查 + 类设计技巧,模拟出类似 TypeScript 接口和抽象类的行为。核心思路是:**用构造函数抛错强制子类实现方法,用静态属性或文档约定声明“契约”**。
用构造器抛错模拟抽象方法
在基类 constructor 中检查子类是否实现了关键方法,未实现则报错:
class Animal {
constructor() {
// 检查 this 是否实现了 speak 方法(且不是 Animal 原型上的)
if (this.constructor === Animal) {
throw new Error('Animal 是抽象类,不能直接实例化');
}
if (typeof this.speak !== 'function' || this.speak === Animal.prototype.speak) {
throw new Error(`子类 ${this.constructor.name} 必须实现 speak() 方法`);
}
}
<p>speak() {
// 占位实现,仅用于检测是否被重写
throw new Error('speak() 必须由子类实现');
}
}</p><p>class Dog extends Animal {
speak() {
return 'Woof!';
}
}</p><p>class Cat extends Animal {
// 忘记实现 speak → 实例化时立刻报错
}</p><p>new Dog(); // ✅ 正常
new Cat(); // ❌ 报错:"子类 Cat 必须实现 speak() 方法"
用静态属性声明“接口契约”
通过静态属性显式列出子类应实现的方法名,便于阅读和工具辅助(如 IDE 提示、单元测试校验):
class Drawable {
constructor() {
const required = this.constructor.requiredMethods || [];
for (const method of required) {
if (typeof this[method] !== 'function') {
throw new Error(`${this.constructor.name} 缺少必需方法: ${method}`);
}
}
}
}
<p>// 约定:所有继承者必须实现这些方法
Drawable.requiredMethods = ['draw', 'getArea', 'toString'];</p><p>class Circle extends Drawable {
draw() { /<em> ... </em>/ }
getArea() { return Math.PI * this.r ** 2; }
toString() { return <code>Circle(r=${this.r})</code>; }
// ✅ 全部实现</p><p>class Rectangle extends Drawable {
draw() { /<em> ... </em>/ }
getArea() { return this.w * this.h; }
// ❌ 忘了 toString → new Rectangle() 时抛错
}
配合 JSDoc + TypeScript 类型提示(推荐组合)
即使纯 JS 项目,也可用 JSDoc 注释 + VS Code + TypeScript 插件获得接口级提示:
立即学习“Java免费学习笔记(深入)”;
/**
* @abstract
* @interface
*/
class Shape {
/**
* @abstract
* @returns {number}
*/
getArea() {}
<p>/**</p><ul><li>@abstract</li><li>@returns {string}
*/
toString() {}
}</li></ul><p>/*<em> @extends {Shape} </em>/
class Square extends Shape {
constructor(side) {
super();
this.side = side;
}</p><p>getArea() { return this.side ** 2; } // ✅ 自动提示:需实现 toString
// toString() {} // ⚠️ TS/IDE 会标黄警告:未实现抽象方法
}
这样既保持 JS 运行时灵活性,又获得接近 TypeScript 的开发体验。
小技巧:用 Symbol 避免命名冲突
若担心子类意外覆盖基类占位方法,可用唯一 Symbol 作“抽象标记”:
const ABSTRACT = Symbol('abstract');
<p>class PaymentProcessor {
constructor() {
if (this.process[ABSTRACT]) {
throw new Error('PaymentProcessor 是抽象类');
}
}</p><p>process() {
// 占位方法,不可调用
if (this.process[ABSTRACT]) {
throw new Error('process() 必须由子类重写');
}
}
}</p><p>// 子类重写时清除标记
class StripeProcessor extends PaymentProcessor {
process() {
// 清除抽象标记(可选,仅用于更严格检测)
delete this.process[ABSTRACT];
return 'Charging via Stripe...';
}
}
不复杂但容易忽略:关键不在语法糖,而在用运行时断言 + 显式契约 + 工具协同,把“该做什么”的约定真正落地到执行环节。


















