Skip to content

JavaScript 的单例模式

Published:

为什么需要单例模式

在开发过程中,保持代码的正确性至关重要。主要有两个方面可以确保这一点:

  1. 唯一性保证:确保代码结构能强制执行标准,避免错误的产生。也就是说,你的代码只能按照规定的方式工作,从而防止出现不符合标准的代码。
  2. 错误提示:在开发阶段提供及时的错误提示。虽然不能绝对保证代码完全正确,但能在开发过程中发现并解决潜在的错误,避免这些错误流入生产环境。

核心思想

一个构造函数只能创建一个实例。通过这种方式,我们可以确保在整个应用程序中,某个类只有一个对象被创建。这样做的好处是可以减少资源消耗,控制对共享资源的访问,避免状态不一致等问题。

如果这些保证不到位,可能会导致错误的代码漏到用户端,增加调试和修复的成本。

实现单例模式的几种方法

1. 使用闭包(JavaScript)

在 JavaScript 中,可以使用闭包来模拟私有构造函数,从而实现单例模式。闭包允许我们封装构造函数并确保外部无法直接创建新实例。

const Singleton = (function () {
  let instance;

  function createInstance(arg) {
    return { arg };
  }

  return {
    getInstance: function (arg) {
      if (!instance) {
        instance = createInstance(arg);
      }
      return instance;
    },
  };
})();

// 使用
const instance1 = Singleton.getInstance("First Argument");
const instance2 = Singleton.getInstance("Second Argument");

console.log(instance1.arg); // 'First Argument'
console.log(instance2.arg); // 'First Argument'(实例不变)
console.log(instance1 === instance2); // true

2. 使用 TypeScript

class Singleton {
  private static instance: Singleton;

  private constructor(public arg: string) {}

  public static getInstance(arg: string): Singleton {
    if (!Singleton.instance) {
      Singleton.instance = new Singleton(arg);
    }
    return Singleton.instance;
  }

  public getArg(): string {
    return this.arg;
  }
}

// 使用
const instance1 = Singleton.getInstance("First Argument");
const instance2 = Singleton.getInstance("Second Argument");

console.log(instance1.getArg()); // 'First Argument'
console.log(instance2.getArg()); // 'First Argument'(实例不变)
console.log(instance1 === instance2); // true

3. 使用 Proxy 实现单例模式

class Singleton {
  name = "";

  constructor(name) {
    this.name = name;
  }

  sayHi() {
    console.log("Hi " + this.name);
  }
}

const SingletonProxy = new Proxy(Singleton, {
  instance: null,
  construct(target, args) {
    if (!this.instance) {
      this.instance = new target(...args);
    }
    return this.instance;
  },
});

// 使用
const instance1 = new SingletonProxy("First Argument");
const instance2 = new SingletonProxy("Second Argument");

console.log(instance1); // 'First Argument'
console.log(instance2.getArg()); // 'First Argument'(实例不变)
console.log(instance1 === instance2); // true