JavaScript单例模式本质是手动控制构造函数始终返回同一实例,核心在控制而非声明;需用闭包或静态私有字段缓存实例,并统一通过getInstance访问,避免new绕过控制。

JavaScript 中没有原生的“单例类”语法,所谓单例模式,本质是**手动控制一个构造函数只返回同一个实例对象**,关键在“控制”而非“声明”。
为什么 new 两次不等于两个实例?
单例的核心不是避免 new,而是确保多次调用构造逻辑后,始终返回同一引用。常见错误是只做了一次初始化但没缓存结果,或用了闭包却没暴露统一入口。
- 直接在构造函数里判断
this instanceof Singleton无效——每次new都会创建新对象 - 用全局变量缓存(如
window.instance)污染作用域,且无法模块化 - ES6 模块默认单例,但仅适用于无参、无状态的导出;有初始化逻辑时仍需手动控制
最稳妥的单例封装:闭包 + 静态属性
利用函数作用域保存私有实例,通过静态方法提供受控访问。比模块导出更灵活,支持延迟初始化和参数传入。
function Logger() {
if (Logger.instance) {
return Logger.instance;
}
this.logs = [];
Logger.instance = this;
}
Logger.prototype.add = function(msg) {
this.logs.push(msg);
};
Logger.getInstance = function() {
return Logger.instance || new Logger();
};
调用时统一走 Logger.getInstance(),而不是 new Logger()。这样即使后续修改构造逻辑(比如加异步初始化),也只需改 getInstance 内部。
立即学习“Java免费学习笔记(深入)”;
ISite企业建站系统是为懂点网站建设和HTML技术的人员(例如企业建站人员)而开发的一套专门用于企业建站的开源免费程序。本系统采用了全新的栏目维护模式,内容添加过程中,前后台菜单是一样的,需要维护前台某个栏目的内容,只需要进后台相应栏目即可,一般的企业人员只需要查看简易的说明就可以上手维护网站内容。通过自由度极高的模板系统,可以适应大多数情况的界面需求,后台带有标签生成器,建站只需要构架好HTM
ES6 Class + 私有字段的现代写法
用 #instance 私有字段 + 静态 getter,语义更清晰,且避免意外覆盖静态属性:
class Database {
static #instance = null;
constructor(url) {
if (!Database.#instance) {
this.url = url;
this.connection = null;
Database.#instance = this;
}
return Database.#instance;
}
static getInstance(url) {
if (!Database.#instance) {
return new Database(url);
}
return Database.#instance;
}
}
注意:new Database(url) 仍可能绕过控制——必须约定只用 getInstance。TypeScript 可加私有构造器进一步约束,但 JS 运行时无法强制。
真正容易被忽略的点
单例不是万能解耦方案。以下情况反而会让代码更难测、更难维护:
- 实例持有 DOM 引用或定时器,未提供
destroy方法,导致内存泄漏 - 多个模块都调用
getInstance()但传入不同参数(如不同 API 地址),后者会静默覆盖前者 - 在 SSR 环境中,服务端多个请求共享同一实例,造成数据交叉污染
真需要“唯一性”的场景,优先考虑依赖注入或顶层 Provider(如 React Context、Vue provide/inject),而不是硬编码单例逻辑。









