单例模式真正解决的不是“只能 new 一次”,而是统一资源入口

单例模式真正解决的不是"只能 new 一次",而是统一资源入口

看到下面这段代码时,很多人会先记住 a === b 是 true:

js 复制代码
const a = Popup.getInstance();
const b = Popup.getInstance();
console.log(a === b); // true

但面试或实际项目中,更重要的问题是:实例由谁创建?谁保存它?其他代码能不能绕过这个入口?本文用一个打开网页的 Popup 示例,把这条逻辑拆开。源码静态阅读,运行未验证。

先看最小模型

text 复制代码
Popup 类
  ↓
静态属性 ins 保存实例
  ↓
getInstance 统一返回实例
  ↓
按钮事件调用实例方法

单例模式的核心不是魔法,而是资源生命周期的集中管理。

getInstance 做了两件事

js 复制代码
class Popup {
  static ins;

  static getInstance() {
    if (!Popup.ins) {
      Popup.ins = new Popup();
    }
    return Popup.ins;
  }
}

第一件事:检查实例是否存在。

第二件事:不存在时创建,存在时复用。

所以执行顺序是:

text 复制代码
第一次调用:undefined → new Popup() → 保存 → 返回
第二次调用:已有实例 → 不创建 → 直接返回

static 让 getInstance() 在没有实例时也可以直接通过类调用:

js 复制代码
Popup.getInstance();

如果它不是静态方法,就必须先有一个对象,这与"由它负责创建第一个对象"的目标矛盾。

a === b 证明了什么

js 复制代码
const a = Popup.getInstance();
const b = Popup.getInstance();

两次调用返回的都是 Popup.ins。对于对象,=== 比较的是引用,因此 true 表示两个变量指向同一个实例,而不是仅仅"两个对象内容相同"。

这也是单例示例中最直接的验证方式。

和按钮事件连起来

js 复制代码
const openBtn = document.getElementById("openBtn");
openBtn.addEventListener("click", () => {
  a.open("https://www.baidu.com");
});

页面先找到按钮,再监听点击事件。点击发生后,调用之前获取的单例对象。

这体现了一个可迁移的工程思路:

text 复制代码
页面事件只负责触发
业务对象负责执行
实例获取由统一入口负责

这个例子目前并不完整

Popup 类里没有 open(),但点击事件调用了它:

js 复制代码
a.open("https://www.baidu.com");

因此运行时可能出现:

text 复制代码
TypeError: a.open is not a function

源码中被注释的旧写法是:

js 复制代码
window.open("https://www.baidu.com ", "_blank");

它暗示打开网页的行为应该封装到 Popup 中,但当前材料没有给出完整实现。这个问题不能被"单例模式本身正确"掩盖:设计模式解决结构问题,不能自动补齐缺失的方法。

初学者自检表

  • static 属性属于类还是实例?
  • 第一次调用是否真的执行了 new Popup()?
  • 第二次调用是否复用了缓存?
  • a === b 比较的是值还是引用?
  • 点击事件调用的方法是否在类中存在?
  • 是否有人绕过 getInstance() 直接 new Popup()?

单例模式的边界

当前代码通过静态属性实现了"单例式缓存",但没有禁止外部直接调用:

js 复制代码
new Popup();

因此它依赖调用约定,而不是严格限制实例数量。对于很多前端小项目,这种实现足够直观;如果要做更严格的限制,还需要进一步封装构造过程。

最后一个判断

单例模式值得学习的地方,不是背下 getInstance 模板,而是理解资源管理:

当一个对象应该被多个地方共享时,把创建、缓存和访问规则集中起来,能减少重复初始化和状态分散。

这个 Popup 示例还缺少 open() 方法。补齐后再验证弹窗行为,才能把"设计意图"和"实际可运行代码"区分开。运行结果当前未验证。

标签: JavaScript, 单例模式, 设计模式, 前端开发

相关推荐
栉甜1 小时前
Vue2学习内容(上)
前端·javascript·vue.js·学习
shmily麻瓜小菜鸡2 小时前
TypeScript 知识点补充(第三部分:工程化与高级类型)
前端·javascript·typescript
Hilaku3 小时前
前端的技术判断力是怎么练出来的?
前端·javascript·程序员
miofly4 小时前
GitHub 今日推荐|floorplan:AI 读代码自动画交互式架构地图
javascript·开源·github
Dovis(誓平步青云)5 小时前
拖动关键帧角色就抽动,动画编辑器怎样补齐中间状态
开发语言·前端·javascript·机器学习·编辑器
lpfasd1235 小时前
pywebview与Electron
前端·javascript·electron
行走的陀螺仪5 小时前
Claude Code 从入门到实战(一):安装、登录与第一个Vue项目
前端·javascript·vue.js
vipxieliang5 小时前
解构赋值与展开运算符:现代 JS 提效技巧
javascript
2603_965896625 小时前
JavaScript 原型与原型链
开发语言·前端·javascript
晓得迷路了5 小时前
栗子前端技术周刊第 152 期 - Rspack 2.3、TypeScript 7.1 Beta、Bun 1.4.4...
前端·javascript·css