《设计模式?》前端单例模式保姆级教程:用 Class + 闭包各封装一个 LocalStorage 单例,一次学会!

哈喽掘友们,今天给你带来一个面试 + 实战双杀 的设计模式:
单例模式(Singleton Pattern)

别小看它,这玩意儿可是:

  • 设计模式里最基础、最实用的一个。
  • 你要是说不出来,面试官心里:嗯?没用过 Vuex?没写过封装工具?
  • 真正搞明白,工作里封装 storage、全局配置、状态管理都能用到。

别担心,今天咱直接手把手演示:

✅ 什么是单例?

✅ 为啥要单例?

✅ 怎么用 class 写单例?

✅ 怎么用 闭包 写单例?

✅ 两种写法有啥区别?

看完保证会用,还能跟面试官对吹不虚场!


什么是单例?

官方八股文:

单例模式是一种创建型设计模式 ,核心目的是:保证某个类在整个系统中只有一个实例,并提供全局访问点。

人话:

一个类只能生一个娃,全局就这一个娃谁都得用它。


单例能干嘛?

常见场景:

  • 日志收集器(只有一个人负责记日志)
  • 配置中心(配置信息全局共享)
  • 状态管理(Vuex、Redux 本质上都是单例 store)
  • 工具封装(localStorage、sessionStorage 封装)
  • 连接池(后端里用得多,前端了解即可)

咱要干啥?

今天来动手封装一个**Storage 单例**,干嘛用?

  • 内部基于浏览器的 localStorage。
  • 提供两个方法:setItem(key, value)、getItem(key)。
  • 自动序列化/反序列化,避免忘写 JSON.stringify。

单例核心设计思路(class版)

咱先搞明白单例是怎么落地的,最常见就是:

1️⃣ 一个 static 静态属性 instance,负责 hold 住唯一实例。

2️⃣ 一个 static getInstance() 静态方法:

  • 如果没实例,就 new 一个挂上去;
  • 有了就直接 return。
    3️⃣ 使用时:Storage.getInstance() 永远拿到同一个对象。

✅ 上代码!Storage 单例 class 版

js 复制代码
class Storage {
  // 静态属性,hold 全局唯一实例
  static instance = null;

  // 静态方法,获取唯一实例
  static getInstance() {
    if (!Storage.instance) {
      Storage.instance = new Storage();
    }
    return Storage.instance;
  }

  // 设置
  setItem(key, value) {
    localStorage.setItem(key, JSON.stringify(value));
  }

  // 获取
  getItem(key) {
    const value = localStorage.getItem(key);
    try {
      return JSON.parse(value);
    } catch (e) {
      return value;
    }
  }
}

// 使用
const s1 = Storage.getInstance();
const s2 = Storage.getInstance();

s1.setItem('name', '掘金小白');
console.log(s2.getItem('name')); // 掘金小白

console.log(s1 === s2); // true,确实只有一个实例

✅ 分析一下

  • static:挂在类上,不在原型上,不属于实例。
  • getInstance():外部想要实例,都得走这里。
  • setItem/getItem:普通实例方法,挂在 prototype 上,实例共享。

这就是单例最经典的写法,大厂面试只要问到 "单例",脱口而出这套就没错。


那闭包版是啥?为啥要有闭包版?

别急,闭包版也是面试爱问,原因是:

  • 早期没有 class,只能用函数 + 闭包模拟单例。
  • JS 的闭包很适合保存私有变量(就是把唯一实例藏起来)。
  • 有时候为了更灵活,也会用闭包来包装一些状态。

✅ 来,闭包版也走一个

思路:

  • 外层一个 IIFE(立即执行函数),内部声明唯一实例 let instance。
  • 内部定义构造函数 StorageBase。
  • StorageBase 构造里判断:有实例就直接 return,否则挂自己。
  • 返回这个构造函数。

看

js 复制代码
const Storage = (function() {
  let instance;

  function StorageBase() {
    if (instance) {
      return instance;
    }
    instance = this;
  }

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

  StorageBase.prototype.getItem = function(key) {
    const value = localStorage.getItem(key);
    try {
      return JSON.parse(value);
    } catch (e) {
      return value;
    }
  };

  return StorageBase;
})();

// 使用
const s1 = new Storage();
const s2 = new Storage();

s1.setItem('job', '前端摸鱼王');
console.log(s2.getItem('job')); // 前端摸鱼王

console.log(s1 === s2); // true,闭包也 hold 住唯一实例

这俩写法有啥区别?

点 class 版 闭包版
核心依赖 ES6 class + static 函数 + 闭包
实现思路 静态属性存实例 闭包变量存实例
使用姿势 Storage.getInstance() new Storage()
场景 模块化/现代前端常用 老代码、灵活封装、函数式玩具

✅ 有啥坑要注意?

  • 闭包版一定别写错!不要 StorageBase 里 new StorageBase(),不然直接死递归爆栈(很多人翻车在这)。
  • 有些同学喜欢直接 export default new Storage(),这是模块化的懒汉式,天然单例,也没问题。
  • 闭包写法有点"老派",但考察你基础掌握得牢不牢。

总结一波

单例模式 = 一个类只能造一次,想用都找它要,不会多生孩子。

核心关键点

  • static + getInstance()(现代写法)
  • 闭包 hold 住唯一实例(经典写法)
  • 用在全局唯一、状态管理、工具封装

必背台词

单例是最简单的设计模式之一,核心就是保证全局唯一实例,提供可控访问点。实际场景可以是配置中心、缓存池、日志、全局状态管理等,既节省资源又利于统一管理。


面试杀招

面试官:会单例吗?

你:会啊,用 class 的话可以用 static 属性保存唯一实例,getInstance 做懒汉式;用闭包也可以 hold 实例,外面拿不到内部状态,保证只生成一次。

面试官:🫡,这小子还行!


写在最后

单例说简单真不难,但能写对,能讲清楚,能灵活切换实现方式,你就真的比很多人多拿一分 offer。

要是这篇帮你理清了单例,记得一键三连:

点个赞

收藏回头抄

留言:要不要我再写个「模块化 + TS 单例」的进阶版?

我是小阳,咱下次掘金再见,摸鱼人冲!

相关推荐
怕浪猫3 小时前
GEO 优化到底是什么?AI时代内容创作者必须懂的新技能
算法·面试·github
沙漠之主7 小时前
C++编程教学设计资料:从入门到实战的完整课程方案
java·前端·c++
hasty7 小时前
不上传新包,也能改变用户拿到的版本:npm dist-tag 的 OIDC 权限治理
前端·npm·node.js
Csvn7 小时前
diff 算法(虚拟 DOM Reconciliation)
前端
前端snow8 小时前
ai agent --- 文件存储
前端
Frag0ut8 小时前
Chrome四大版本获取及共存指南:Stable/Beta/Dev/Canary
前端·chrome·浏览器·dev·beta·canary·共存版
IT大白鼠9 小时前
搜索系列 · 第 08 篇——面试收官:高频题与全景总结
elasticsearch·面试·nosql
IT_陈寒10 小时前
SpringBoot自动配置坑了我一把,原来是这样绕过去的
前端·人工智能·后端
广州华水科技10 小时前
单北斗GNSS变形监测在大坝安全监测中的应用与优势
前端
zeng不错10 小时前
B站 173 个投稿活动看到眼瞎?我写了个扩展,主打一个精准打击
前端