面试必问!用单例模式封装 LocalStorage,彻底搞懂 JS 单例原理

8 阅读3分钟

在前端开发中,localStorage 是我们最常用的本地存储方案之一。
但很多人平时都是直接用,没有去封装过,也没想过要用设计模式来优化。

其实在中大型项目里,localStorage 很容易被多处调用,如果到处都是零散的读写操作,不仅不好维护,还容易踩坑。
这时候,就可以用 单例模式 来把它封装起来,让它既高效又好用。


单例模式到底是啥?

单例模式(Singleton Pattern) 的核心就是一句话:一个类只能有一个实例,并且自己负责创建这个唯一实例。

比如:

  • 全局配置中心
  • 缓存池
  • 数据库连接
  • 日志管理器

这些场景都需要“唯一性”,单例模式就是最适合的解决方案。


为什么要对 localStorage 做单例封装?

  • 保证数据一致性
    有了单例,多个地方拿到的都是同一个实例,状态一致,读写不乱。
  • 节省性能开销
    不会重复 new,省了内存,也更快。
  • 更好维护
    后续想要加日志、埋点、或者切换存储方案,只用改这里。

如何用 JavaScript 实现单例?

在前端项目里,实现单例最常用的有两种思路:

  1. ES6 class + 静态属性/方法
  2. 闭包 来做私有变量

实现方案一:ES6 class 单例

先来看用 class 怎么写。

class Storage {
  static instance;

  constructor() {
    console.log('Storage 实例化');
  }

  static getInstance() {
    if (!Storage.instance) {
      Storage.instance = new Storage();
    }
    return Storage.instance;
  }

  getItem(key) {
    return localStorage.getItem(key);
  }

  setItem(key, value) {
    localStorage.setItem(key, value);
  }
}

// 测试
const storage1 = Storage.getInstance();
const storage2 = Storage.getInstance();

console.log(storage1 === storage2); // true

storage1.setItem('username', 'Tom');
console.log(storage2.getItem('username')); // Tom

要点回顾:

  • 用静态属性 instance 保存唯一实例。
  • getInstance 方法是整个单例的入口,保证永远只 new 一次。
  • 无论多少次调用,拿到的都是同一个 Storage 实例。

实现方案二:闭包单例

用闭包实现也很经典,尤其是在没有 class 的老项目里。

function StorageBase() {}

StorageBase.prototype.getItem = function(key) {
  return localStorage.getItem(key);
};

StorageBase.prototype.setItem = function(key, value) {
  localStorage.setItem(key, value);
};

const Storage = (function() {
  let instance = null;
  return function() {
    if (!instance) {
      instance = new StorageBase();
    }
    return instance;
  };
})();

// 测试
const storage1 = new Storage();
const storage2 = new Storage();

console.log(storage1 === storage2); // true

storage1.setItem('username', 'Tom');
console.log(storage2.getItem('username')); // Tom

要点回顾:

  • 用 IIFE(立即执行函数)生成一个私有作用域。
  • instance 是自由变量,只会存在一份,外部访问不到。
  • 每次调用 new Storage() 都会先判断有没有实例,有就返回已有的。

单例模式的小结

单例模式的核心就一句话:

自己把自己实例化,并保证只有一次,后续都复用同一个。

在实际项目里,像全局配置、本地缓存、事件总线这类全局资源,几乎都可以考虑单例化封装。
不仅能提高性能,还能大大减少后期维护的心智负担。


Tips:什么时候不该用单例?

并不是所有场景都适合用单例。
如果一个类本身就是需要多个独立实例(比如多个弹窗、多个请求对象),硬生生做成单例反而会带来状态混乱。
所以是否用单例,关键看你的业务有没有“全局唯一性”的需求。


写在最后

单例模式不是什么高深难懂的东西,理解了核心思想后,你会发现它几乎贯穿在各种框架、库和日常业务中。
别看只是封装一个 localStorage,面试官问到的时候,能把思路讲清楚,绝对能为你的答题加分不少。


有用的话,点个赞或者收藏支持一下!
后面我会持续分享更多前端设计模式和最佳实践,记得关注~