TypeScript 中的单例模式

单例模式(单体):一个类有且只实例化一个实例对象

更具体的说:

如果一个类对外只提供一个对象实例,并且对外提供一个唯一可以访问该对象的方法或者属性,

那么这样就可以保证该对象的唯一性

那为啥不直接声明一个对象而是通过类实例化出一个对象?

显然是最大限度的利用面向对象的思想:更具封装性,更易于扩展

场景#

何时使用单例模式呢?当然是从单例模式本身的重要特征考虑:唯一性

唯一:独立无二,有且仅有一个。符合此特征就可以考虑使用单例模式。

例如:

场景一:客户端存储

localStore/sessionStory => 同源策略,同协议,同域名,同端口共用同一份数据 => 单例模式

场景二:全局状态管理

全局状态 store => SPA 共用全局属性和方法(唯一的实例对象) => 单例模式 (react-redux , vuex 等)

当然,还有很多场景~譬如模拟一个地球,模拟太阳系什么的

模式#

在 typescript 中如何实现单例模式呢?就以场景一中的客户端存储为例:

第一步:私有化构造器

class LocalStorageLayz {
  // 私有化构造器
  private constructor() {}
}

第二步:对外提供可访问的方法,调用该方法可以得到当前类的唯一对象实例

class LocalStorageLayz {
  // 静态属性 引用 LocalStotrage 类的唯一实例对象
  static localStorage: LocalStorageLayz;

  // 私有化构造器
  private constructor() {}

  // 提供一个外部可访问的的静态方法
  public static getInstance() {
    if (!this.localStorage) {
      this.localStorage = new LocalStorageLayz();
    }

    return this.localStorage;
  }

  // 实例方法
  public getItem(key: string) {
    let val = localStorage.getItem(key);
    return val !== null ? JSON.parse(val) : null;
  }

  // 实例方法
  public setItem(key: string, val: any) {
    localStorage.setItem(key, JSON.stringify(val));
  }
}

第三步:外部调用第二步对外提供的方法,得到唯一实例

const instanceLayz = LocalStorageLayz.getInstance();

instanceLayz.setItem("instanceLayz", { "1": 1, "2": 3 });

let val = instanceLayz.getItem("instanceLayz");

console.log("inatanceLayz", val);

其中第二步中,可以通过对外提供的静态属性直接引用实例对象,也可以通过对外提供的静态方法去初始化静态属性。如下:

class LocalStorage {
  // 静态属性 引用 LocalStotrage 类的唯一实例对象
  static localStorage: LocalStorage = new LocalStorage();

  // 私有化构造器
  private constructor() {}

  // 实例方法
  public getItem(key: string) {
    let val = localStorage.getItem(key);
    return val !== null ? JSON.parse(val) : null;
  }

  // 实例方法
  public setItem(key: string, val: any) {
    localStorage.setItem(key, JSON.stringify(val));
  }
}

区别在于,通过静态方法可以手动控制挂载唯一实例的时机会,而通过静态属性则会在类加载时挂载到静态属性上

const instance = LocalStorage.localStorage;
instance.setItem("instance", { "3": 3, "4": 4 });
const value = instance.getItem("instance");
console.log("instance", value);

扩展#

JavaScript 实现

function Singleton() {}

Singleton.getInstance = function (...args) {
  if (!Singleton.instance) {
    Singleton.instance = new Singleton();
  }

  return Singleton.instance;
};

Singleton.instance = null;

const a = Singleton.getInstance();
const b = Singleton.getInstance();

console.log(a === b); // true

闭包实现

const Singleton = (function () {
  let instance = null;

  return function () {
    if (instance) {
      return instance;
    }

    return (instance = this);
  };
})();

const a = new Singleton();
const b = new Singleton();

console.log(a === b); // true

总结#

简单实现了通过 typescript 中的静态成员(静态方法/静态属性)实现 单例模式的具体逻辑:首先明确单例模式的的重要特征:单个实例;然后如何通过保证一个类有且只有一个实例呢?

  • 对外提供一个静态属性或者静态方法;
  • 在类挂载的时候实例化单例或者通过提供的静态方法手动实例化单例

也许你会疑惑为啥单例为啥不直接放到原型上去?而是放在类的静态成员上呢?这里前提是得知道类的静态成员以及 JavaScript 中的原型机制!

references#

posted @   shanejix  阅读(2726)  评论(0编辑  收藏  举报
编辑推荐:
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
阅读排行:
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
点击右上角即可分享
微信分享提示
主题色彩