JavaScript 单例模式详解:确保类的唯一实例

什么是单例模式?

确保一个类只有一个实例存在,那就要求它的构造方法一定不能是public公开的,即不能被外界实例化。那它的构造方法只能是private, 并且拥有一个当前类的静态成员变量,后面他要求向整个系统提供这个实例,即我们要再提供一个静态的方法,向外界提供当前类的实例,当前实例只能在内部进行实例化,不能够放到外面去。

为什么需要单例模式?

在实际开发中,某些对象我们只需要一个实例,例如:
  • 浏览器中的 localStorage 对象
  • 全局状态管理
  • 配置管理器
  • 数据库连接池

创建多个实例不仅浪费资源,还可能导致数据不一致的问题。

ES6 类实现单例模式

让我们先看一个普通的 Storage 类:

class Storage {
  constructor() {
    console.log(this, '~~~');
  }
  
  getItem(key) {
    // 获取数据
  }
  
  setItem(key, value) {
    // 设置数据
  }
}

const storage1 = new Storage();
const storage2 = new Storage();
console.log(storage1 === storage2, '~~~'); // false

上面的代码每次使用 new 关键字都会创建一个新的实例,导致 storage1 和 storage2 是不同的对象。这在某些场景下可能会导致问题,比如多个实例操作同一个资源时的数据不一致。

使用静态方法实现单例模式

ES6 的 class 语法提供了 static 关键字,可以用来实现单例模式:

class Storage {
  static instance;
  
  constructor() {
    console.log(this, '~~~');
  }
  
  // 静态方法
  static getInstance() {
    if (!Storage.instance) {
      Storage.instance = new Storage();
    }
    return Storage.instance;
  }
  
  getItem(key) {
    return localStorage.getItem(key);
  }
  
  setItem(key, value) {
    return localStorage.setItem(key, value);
  }
}

const storage1 = Storage.getInstance();
const storage2 = Storage.getInstance();
console.log(storage1 === storage2, '~~~'); // true
storage1.setItem('name', '卢老板');
console.log(storage1.getItem('name'), '~~~'); // 卢老板
console.log(storage2.getItem('name'), '~~~'); // 卢老板

这里的关键点是:

  • 使用 static instance 作为静态属性保存唯一实例
  • 提供 static getInstance() 方法检查实例是否存在:如果不存在,则创建新实例;如果存在,则返回已有实例

通过 getInstance() 方法而非直接使用 new 来获取实例。

单例模式的优势

  • 资源节约:只创建一个实例,减少内存占用
  • 避免冲突:确保对共享资源的一致访问
  • 全局访问点:提供统一的访问入口,方便管理
  • 延迟初始化:实例在首次使用时才被创建(懒加载)

单例模式的实际应用:封装 localStorage 操作

正如我们的示例所示,单例模式非常适合封装浏览器的 localStorage API:

class StorageManager {
  static instance;
  
  static getInstance() {
    if (!StorageManager.instance) {
      StorageManager.instance = new StorageManager();
    }
    return StorageManager.instance;
  }
  
  getItem(key) {
    return localStorage.getItem(key);
  }
  
  setItem(key, value) {
    return localStorage.setItem(key, value);
  }
  
  removeItem(key) {
    return localStorage.removeItem(key);
  }
  
  clear() {
    return localStorage.clear();
  }
}

// 使用
const storage = StorageManager.getInstance();
storage.setItem('user', JSON.stringify({name: '张三', age: 25}));

单例模式是一种简单但强大的设计模式,它确保一个类只有一个实例,并提供全局访问点。在JavaScript中,可以通过ES6的static关键字或闭包来实现单例模式。

单例模式适用于需要协调行为的场景,如管理共享资源、状态管理等。在实际开发中,我们应该根据具体需求选择是否使用单例模式,避免过度使用导致系统耦合度过高。

在 Typescript 中实现单例模式

在 TypeScript 中实现单例模式,可以通过创建一个类并确保只有一个实例被创建。以下是一个简单的示例:

class OAuthUtils {
  private static instance: OAuthUtils;
  // 私有构造函数,防止外部实例化
  private constructor() {}
  // 获取单例实例的方法
  public static getInstance(): OAuthUtils {
    if (!OAuthUtils.instance) {
      OAuthUtils.instance = new OAuthUtils();
    }
    return OAuthUtils.instance;
  }
  // 示例方法
  public authenticate() {
    // 认证逻辑
  }
}
// 导出单例
export const oauthUtils = OAuthUtils.getInstance();

使用示例

import { oauthUtils } from './oauthUtils';
oauthUtils.authenticate();

以上代码确保 `OAuthUtils` 只能被实例化一次,并通过 `getInstance` 方法访问该实例。

在 Typescript 中实现多例模式

在 JavaScript 中,多例模式(Multiton Pattern)是一种设计模式,它确保一个类在程序中只会被实例化一次,但可以创建多个独立的实例,每个实例都有唯一的标识符。与单例模式不同,单例模式只允许创建一个实例,而多例模式允许根据不同的键创建多个实例。

class Multiton {
  static instances = {};
  constructor(name) {
    this.name = name;
  }
  static getInstance(name) {
    if (!this.instances[name]) {
      this.instances[name] = new Multiton(name);
    }
    return this.instances[name];
  }
}

使用示例

const instanceA = Multiton.getInstance('A');
const instanceB = Multiton.getInstance('B');
const instanceC = Multiton.getInstance('A'); // 获取同一个实例A
console.log(instanceA === instanceC); // true
console.log(instanceA === instanceB); // false

关键点

  • 每个实例通过唯一的标识符(如字符串)管理。
  • 相同标识符的请求返回同一个实例,不同标识符的请求返回不同实例。
  • 通过静态属性存储实例,可以避免重复创建。

多例模式特别适合需要根据不同上下文创建有限数量的实例的场景。

posted @ 2025-08-25 19:34  李小菜丶  阅读(56)  评论(0)    收藏  举报