单例模式确保类仅有一个实例并提供全局访问点,常用于配置、日志等共享资源管理;核心是控制创建逻辑,常见实现有闭包封装、ES6静态属性及最推荐的模块级单例。

单例模式确保一个类只有一个实例,并提供一个全局访问点。它常用于管理共享资源,比如配置对象、日志器、状态管理器等,避免重复创建造成浪费或状态不一致。
单例的核心逻辑
关键在于控制实例的创建过程:第一次调用时生成唯一实例,之后每次都返回这个已有实例,不再新建。
- 用私有变量缓存实例(通常用闭包或静态属性)
- 提供一个统一的获取方法(如 getInstance())
- 禁止外部通过 new 直接构造(JavaScript 中靠约定或 WeakMap + 构造检查实现“私有化”)
基础实现方式(ES5/ES6 兼容)
最常用的是利用闭包封装实例:
const Singleton = (function() {
let instance = null;
function createInstance() {
return { data: 'shared config', getTime: () => new Date() };
}
return {
getInstance() {
if (!instance) {
instance = createInstance();
}
return instance;
}
};
})();
// 使用
const a = Singleton.getInstance();
const b = Singleton.getInstance();
console.log(a === b); // true
ES6 Class + 静态属性写法(更直观)
借助 class 和静态属性,结构更清晰:
立即学习“Java免费学习笔记(深入)”;
class Logger {
constructor() {
if (Logger.instance) {
return Logger.instance;
}
this.logs = [];
Logger.instance = this;
}
log(msg) {
this.logs.push(`[${new Date().toISOString()}] ${msg}`);
}
}
// 手动初始化一次(或延迟到首次调用)
Logger.instance = null;
// 使用
const logger1 = new Logger();
const logger2 = new Logger();
console.log(logger1 === logger2); // true
现代推荐:模块级单例(最自然)
JavaScript 模块本身是单例——每个模块文件只执行一次,导出对象天然唯一:
// logger.js
export const logger = {
logs: [],
log(msg) {
this.logs.push(`[${new Date().toISOString()}] ${msg}`);
}
};
// 在任何地方 import,拿到的都是同一个对象
// import { logger } from './logger.js';
这种写法简洁、可靠、无需额外控制逻辑,是实际项目中最常用也最推荐的方式。
基本上就这些。单例不复杂但容易忽略边界情况,比如多线程(Web Worker)、服务端 SSR 环境下需注意模块隔离。日常前端开发中,优先用模块导出即可。











