《设计模式?》前端单例模式保姆级教程:用 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()
场景 模块化/现代前端常用 老代码、灵活封装、函数式玩具

✅ 有啥坑要注意?

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

总结一波

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

核心关键点

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

必背台词

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


面试杀招

面试官:会单例吗?

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

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


写在最后

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

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

点个赞

收藏回头抄

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

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

相关推荐
随风一样自由6 小时前
【前端独角兽】刚进入前端领域的前端开发用jQuery还是Vue3?
前端·javascript·vue3·jquery
IMPYLH7 小时前
HTML 的 <data> 元素
前端·html
YHHLAI7 小时前
[特殊字符] 面试中的 Promise —— 从入门到精通
前端·javascript·面试
weixin_BYSJ19878 小时前
SpringBoot + MySQL 乒乓球运动员信息管理系统项目实战--附源码04954
java·javascript·spring boot·python·django·flask·php
沪飘大军8 小时前
ll-llm:一个零依赖、可插拔的 OpenAI 兼容 LLM 代理
javascript
kyriewen9 小时前
我扒了 GPT-5.6 的全部编程跑分——有一项 OpenAI 没敢放出来
前端·gpt·ai编程
前端H10 小时前
微前端 v3 架构升级:从加载隔离到状态协同
前端·架构·状态模式
山河木马10 小时前
GPU自动处理专题3-NDC怎么映射成屏幕像素坐标(视口变换)
javascript·webgl·图形学
陆枫Larry11 小时前
小程序包体积优化:用 SVG 图片替换 iconfont,并保留 CSS 控色能力
前端
Appthing11 小时前
在 TanStack Start 中使用 VitePWA 的正确配置
javascript