《设计模式?》前端单例模式保姆级教程:用 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 单例」的进阶版?

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

相关推荐
G等你下课26 分钟前
React 路由懒加载入门:提升首屏性能的第一步
前端·react.js·前端框架
谢尔登1 小时前
【React Native】ScrollView 和 FlatList 组件
javascript·react native·react.js
蓝婷儿1 小时前
每天一个前端小知识 Day 27 - WebGL / WebGPU 数据可视化引擎设计与实践
前端·信息可视化·webgl
然我1 小时前
面试官:如何判断元素是否出现过?我:三种哈希方法任你选
前端·javascript·算法
OpenTiny社区2 小时前
告别代码焦虑,单元测试让你代码自信力一路飙升!
前端·github
kk_stoper2 小时前
如何通过API查询实时能源期货价格
java·开发语言·javascript·数据结构·python·能源
pe7er2 小时前
HTTPS:本地开发绕不开的设置指南
前端
晨枫阳2 小时前
前端VUE项目-day1
前端·javascript·vue.js
江山如画,佳人北望2 小时前
SLAM 前端
前端
患得患失9492 小时前
【前端】【Iconify图标库】【vben3】createIconifyIcon 实现图标组件的自动封装
前端