ES6类实现单例模式需主动控制实例化过程,核心是静态私有实例+静态工厂方法(如getInstance),或利用模块级闭包导出单例,也可用Symbol/WeakMap增强构造限制。

ES6 类实现单例模式的核心是:**控制类的实例化过程,确保全局仅存在一个实例,并提供统一的访问入口**。不能靠类本身“自动”保证单例,必须在类内部或外部封装逻辑来拦截重复构造。
静态私有实例 + 静态工厂方法
这是最常用、最清晰的方式。利用 static 属性保存唯一实例,用静态方法(如 getInstance())负责创建和返回该实例:
class Logger {
constructor() {
if (Logger.instance) {
return Logger.instance;
}
this.logs = [];
Logger.instance = this;
}
static getInstance() {
if (!Logger.instance) {
Logger.instance = new Logger();
}
return Logger.instance;
}
log(message) {
this.logs.push(`[${new Date().toISOString()}] ${message}`);
}
}
// 使用
const logger1 = Logger.getInstance();
const logger2 = Logger.getInstance();
console.log(logger1 === logger2); // true
⚠️ 注意:constructor 中的 return this 不会阻止 new 调用,但能避免重复初始化;真正可靠的是只通过 getInstance 获取实例,不直接 new Logger()。
闭包封装 + 导出单例对象(推荐用于模块级)
借助 ES6 模块的单例特性(模块只执行一次),把类定义在闭包内,导出已创建好的实例。更安全,天然防止外部 new:
立即学习“Java免费学习笔记(深入)”;
// logger.js
class Logger {
constructor() {
this.logs = [];
}
log(message) {
this.logs.push(`[${new Date().toISOString()}] ${message}`);
}
}
// 模块作用域内只执行一次
const instance = new Logger();
export default instance;
使用时直接导入:
import logger from './logger.js'; import logger2 from './logger.js'; console.log(logger === logger2); // true
使用 Symbol 或 WeakMap 做构造限制(增强防护)
若需严格禁止外部 new,可用私有标识符校验:
const singletonKey = Symbol('singleton');
class Database {
constructor(key) {
if (key !== singletonKey) {
throw new Error('Use Database.getInstance() instead of new Database()');
}
this.connection = 'connected';
}
static getInstance() {
if (!Database._instance) {
Database._instance = new Database(singletonKey);
}
return Database._instance;
}
}
- 外部调用
new Database()会抛错 - 只有
getInstance能传入正确 key,获得实例 - 比单纯依赖文档约定更健壮
为什么不直接在 class 上加 static 实例属性并省略 getInstance?
可以,但不够灵活:
class Config {
static instance = new Config(); // 立即执行,无法延迟初始化
constructor() {
this.data = { env: 'prod' };
}
}
问题在于:实例在模块加载时就创建,无法按需初始化(比如依赖异步配置)。所以带判断的 getInstance 更通用。


















