You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在TypeScript/JavaScript中优雅抽象并复用单例模式?

优雅实现TypeScript/JavaScript多类单例抽象

针对你的问题,这里提供两种优雅的方案,既能抽象复用单例逻辑,又能强制新增类实现单例:

方案一:抽象单例基类(TypeScript优先)

通过定义抽象基类封装单例逻辑,子类继承后自动获得单例能力,同时限制直接实例化:

abstract class Singleton<T> {
  private static _instance: any;

  // 用protected代替private,允许子类继承构造函数
  protected constructor() {
    // 防止通过new直接实例化子类
    if ((this.constructor as typeof Singleton)._instance) {
      throw new Error('请通过getInstance()获取单例实例');
    }
  }

  // 静态方法,子类继承后自动拥有,每个子类维护独立的实例
  public static getInstance<T>(): T {
    if (!this._instance) {
      this._instance = new this();
    }
    return this._instance as T;
  }
}

子类实现示例

class Car extends Singleton<Car> {
  private _name = "Audi";
  private _color = 'red';
  private _maxSpeed = 150;

  drive() {
    console.log(`WOW! You are riding your cool ${this._color} ${this._name} at ${this._maxSpeed} kmh!`);
  }

  paint(color: string) {
    this._color = color;
  }
}

class Plane extends Singleton<Plane> {
  private _motors = 4;
  private _maxSpeed = 550;

  fly() {
    console.log(`WOW! You are flying your plane with roaring ${this._motors} motors at ${this._maxSpeed} kmh!`);
  }
}

使用方式

const car = Car.getInstance();
car.drive(); // 输出对应内容

const plane = Plane.getInstance();
plane.fly();

// 直接new会抛出错误,强制使用单例
// new Car(); // Error: 请通过getInstance()获取单例实例

方案二:高阶函数包装器(JS/TS通用)

通过包装器函数给普通类注入单例逻辑,无需修改原有类的结构,侵入性更低:

function makeSingleton<T extends new (...args: any[]) => any>(OriginalClass: T): T & { getInstance: () => InstanceType<T> } {
  let instance: InstanceType<T>;

  // 用Proxy拦截new操作,确保只创建一个实例
  const SingletonClass = new Proxy(OriginalClass, {
    construct(target, args) {
      if (!instance) {
        instance = new target(...args);
      }
      return instance;
    }
  });

  // 添加getInstance静态方法
  SingletonClass.getInstance = () => {
    if (!instance) {
      instance = new OriginalClass();
    }
    return instance;
  };

  return SingletonClass as T & { getInstance: () => InstanceType<T> };
}

使用示例

// 定义普通类,无需任何单例相关代码
class Car {
  private _name = "Audi";
  private _color = 'red';
  private _maxSpeed = 150;

  drive() {
    console.log(`WOW! You are riding your cool ${this._color} ${this._name} at ${this._maxSpeed} kmh!`);
  }

  paint(color: string) {
    this._color = color;
  }
}

class Plane {
  private _motors = 4;
  private _maxSpeed = 550;

  fly() {
    console.log(`WOW! You are flying your plane with roaring ${this._motors} motors at ${this._maxSpeed} kmh!`);
  }
}

// 包装成单例类
const SingletonCar = makeSingleton(Car);
const SingletonPlane = makeSingleton(Plane);

// 两种调用方式都返回同一个实例
const car1 = SingletonCar.getInstance();
const car2 = new SingletonCar();
console.log(car1 === car2); // true

const plane1 = SingletonPlane.getInstance();
const plane2 = new SingletonPlane();
console.log(plane1 === plane2); // true

强制单例的保障

  • 抽象基类方案:构造函数设为protected,外部无法直接new子类;基类构造时会检查实例是否存在,防止子类内部违规创建。
  • 包装器方案:Proxy拦截所有new操作,无论调用多少次都返回同一个实例;同时getInstance方法也确保实例唯一。

内容的提问来源于stack exchange,提问作者aviya.developer

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.14 06:05:23