🎯 JavaScript 单例模式(Singleton Pattern)—— 从理论到实战

📖 什么是单例模式?

单例模式是 OOP(面向对象编程) 中最经典的设计模式之一,属于 GoF 23 种设计模式中的一种。

💡 核心思想: 一个类在系统中只能被实例化一次,无论调用多少次,返回的都是同一个实例。

单例模式常用于解决以下场景:

🔥 场景 说明
全局状态管理 整个应用只需要一个管理器(如 Vuex Store、Redux Store)
弹窗/窗口控制 页面中只允许打开一个弹窗窗口
数据库连接池 避免重复创建昂贵的连接资源
缓存管理 全局共享同一份缓存数据

🧠 为什么需要单例模式?

假设我们不用单例模式,每次需要弹窗时都 new 一个新对象:

js 复制代码
const a = new Popup();
const b = new Popup();
console.log(a === b); // ❌ false ------ 这是两个完全不同的实例!

这意味着每次点击按钮都会创建一个新的弹窗对象,如果对象内部持有大量资源(DOM引用、网络连接等),就会造成:

  • 🔴 内存浪费 ------ 重复创建不需要的对象
  • 🔴 状态不一致 ------ 多个实例各自维护状态,互相不同步
  • 🔴 行为不可控 ------ 比如重复打开多个窗口

单例模式正是为了确保全局只有一个实例而存在的。


✅ 单例模式的实现

下面是一个最简洁、最经典的 JavaScript 单例模式实现:

js 复制代码
class Popup {
  static ins; // ⬅️ 静态属性,存储唯一的实例

  static getInstance() {
    if (!Popup.ins) {
      Popup.ins = new Popup(); // 🔄 第一次调用时创建实例
    }
    return Popup.ins; // 🔁 后续调用直接返回已有实例
  }

  open(url) {
    window.open(url, "_blank");
  }
}

🔑 三个关键要素

要素 代码 作用
① 静态属性 static ins 作为类级别的"存储容器",不绑定到任何实例
② 延迟初始化 if (!Popup.ins) 只在首次调用时才创建实例(惰性加载)
③ 静态方法 static getInstance() 提供统一的获取入口,替代 new 关键字

⚠️ 注意: 不要在声明静态属性时直接赋值 static ins = new Popup(),这会导致类加载时就立即实例化,失去了"延迟初始化"的优势。


🧪 验证:真的是同一个实例吗?

js 复制代码
const a = Popup.getInstance(); // 第一次获取
const b = Popup.getInstance(); // 第二次获取

console.log(a === b); // ✅ true ------ a 和 b 指向同一个对象

用一张图来理解:

scss 复制代码
调用 getInstance()
       │
       ▼
  ┌──────────┐
  │ ins 存在? │
  └─────┬────┘
        │
   ┌────┴────┐
   No        Yes
   │          │
   ▼          ▼
 new Popup()  直接返回 ins
   │
   ▼
 赋值给 ins,然后返回

无论调用多少次 getInstance(),始终返回的是同一个 Popup 实例。 🎉


🖥️ 完整实战:单例模式打开窗口

将单例模式应用到实际的页面交互中:

html 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>单例模式打开窗口</title>
</head>
<body>
  <button id="openBtn">打开窗口</button>

  <script>
    // ========== ① 定义单例类 ==========
    class Popup {
      static ins;
      static getInstance() {
        if (!Popup.ins) {
          Popup.ins = new Popup();
        }
        return Popup.ins;
      }
      open(url) {
        window.open(url, "_blank");
      }
    }

    // ========== ② 通过 getInstance 获取实例(代替 new) ==========
    const popup = Popup.getInstance();

    // ========== ③ 绑定事件 ==========
    const openBtn = document.getElementById("openBtn");
    openBtn.addEventListener("click", () => {
      popup.open("https://www.baidu.com");
    });
  </script>
</body>
</html>

🔍 为什么不用 new Popup() 使用 getInstance() 能够保证全局只有一个实例 。如果用 new,每次都会创建新对象,就失去了单例的意义。


🌟 单例模式的优缺点

✅ 优点

  • 🏆 全局唯一 ------ 保证系统中该类只有一个实例
  • 💾 节省资源 ------ 避免重复创建和销毁对象
  • 🔄 状态共享 ------ 所有使用者访问同一个实例,状态天然同步

❌ 缺点

  • 🚫 违反单一职责 ------ 类既要负责业务逻辑,又要管理自己的实例化
  • 🧪 测试困难 ------ 全局状态可能导致测试之间互相影响
  • 🔒 不够灵活 ------ 一旦实现为单例,后续难以扩展为多实例

📝 总结

维度 内容
是什么 保证一个类只有一个实例,并提供全局访问点
怎么实现 static 属性存储实例 + getInstance() 延迟初始化
核心判断 if (!Instance) → 不存在则创建,存在则直接返回
适用场景 弹窗管理、全局状态、连接池、缓存等
一句话 🎯 getInstance() 代替 new,全局只有一个对象

💬 单例模式虽然简单,却是学习设计模式的绝佳入口。掌握了它,你就迈出了从"写代码"到"设计代码"的第一步。

相关推荐
vx_Biye_Design2 小时前
springboot游泳馆系统93765-计算机课程设计、毕业设计
java·javascript·spring boot·后端·python·spring·课程设计
晴天162 小时前
VSCodium 项目介绍
前端·javascript·vscode
世岩清上2 小时前
展厅数字内容同质化严重,怎样打造专属叙事风格?
大数据·前端·javascript·人工智能·html·音视频·展厅改造
持敬chijing2 小时前
Web前后端开发-JavaScript 入门教程
开发语言·前端·javascript
leoZ2312 小时前
2026-09-10-静态扫描连错三次-用运行环境当裁判
前端·javascript·vue.js·人工智能·opencv·机器学习·数据挖掘
Maxkim2 小时前
我把一个"会自己查资料"的 AI 助手塞进了浏览器侧边栏,开源了
前端·javascript
mONESY3 小时前
LangChain 结构化输出进阶:with_structured_output 做了什么,以及它为什么流不起来
javascript
2401_868534783 小时前
网络环境规划核心考点全梳理
网络·设计模式
hanchenxing3 小时前
自动化脚本“卡住无输出“怎么办: 一次 WebSocket 挂死的排查与自愈模板
运维·javascript·chrome·websocket·自动化·自动化爬虫