单例模式确保类只有一个实例并提供全局访问点,在 JavaScript 中通过闭包、ES6 模块或静态属性实现。闭包方式用 IIFE 封装实例,模块方式利用导出唯一性,Class 方式借助 static 属性控制构造逻辑,适用于配置管理、日志器、状态仓库等场景。

单例模式确保一个类只有一个实例,并提供全局访问点。在 JavaScript 中,它不依赖 class 的私有机制或锁,而是靠闭包、模块作用域或静态属性控制实例的唯一性。
用闭包封装唯一实例
这是最经典、兼容性最好的实现方式。通过立即执行函数(IIFE)创建私有作用域,内部缓存实例,外部只能通过返回的函数获取该实例。
示例:
const Singleton = (function() {let instance = null; return function() {if (!instance) {instance = { data: [], add(item) {this.data.push(item); }, getData() { return this.data;} }; } return instance; }; })(); // 使用 const a = Singleton(); const b = Singleton(); console.log(a === b); // true
要点:instance 变量被闭包保护,无法从外部重置;每次调用 Singleton() 都返回同一个对象引用。
立即学习 “Java 免费学习笔记(深入)”;
ES6 模块单例(推荐现代项目)
利用 ES6 模块的“单例加载”特性——模块脚本只执行一次,导出对象天然唯一。
示例(singleton.js):
// singleton.js export const instance = {config: { theme: 'light', lang: 'zh'}, updateConfig(newConf) {Object.assign(this.config, newConf); } };
在任意文件中导入:
import {instance as appConfig} from './singleton.js'; import {instance as logger} from './singleton.js'; console.log(appConfig === logger); // true(同一模块,同一导出)
优势:无需手动判断、无闭包开销、天然支持 tree-shaking 和类型推导,适合配置管理、日志器、状态仓库等场景。
Class + 静态属性实现(兼顾面向对象风格)
适用于需要构造逻辑、继承或 TypeScript 类型约束的场景。用 static 属性保存实例,构造器限制重复创建。
class Database {constructor() {if (Database.instance) {return Database.instance;} this.connection = `conn-${Date.now()}`; Database.instance = this; } query(sql) {return `Executing: ${sql} on ${this.connection}`; } } // 使用 const db1 = new Database(); const db2 = new Database(); console.log(db1 === db2); // true
注意:这种方式在严格模式下 new 调用仍会新建 this,但最终返回的是已存在的 instance;若需彻底阻止重复初始化,可配合 Symbol 或 WeakMap 做更严谨校验。
典型应用场景
- 全局配置中心 :如主题、用户偏好、API 基础路径,避免多处分散维护
- 日志记录器 :统一格式、级别控制和输出目标,防止日志错乱或重复初始化
- 状态管理实例 :如简易版 Store,多个组件共享同一状态快照
- 第三方 SDK 封装 :如支付、地图、埋点 SDK,确保只初始化一次且 API 统一入口
不复杂但容易忽略:单例强调“唯一性”而非“全局变量”。应避免把普通对象挂到 window 上,而要通过可控的获取方式(函数调用 / 模块导入)来使用。






























