单例模式确保类只有一个实例并提供全局访问点,在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 上,而要通过可控的获取方式(函数调用 / 模块导入)来使用。










