javascript单例可通过构造函数内缓存实例或闭包封装实现:前者用静态属性logger.instance判断复用,后者用iife闭包隐藏instance并返回工厂函数,均确保多次调用返回同一对象。

JavaScript 构造函数本身无法阻止多次 new 调用,但可以通过内部逻辑判断 + 实例缓存,让每次调用都返回同一个对象引用——这就是“逻辑单例”。关键不是禁止 new,而是让 new 的结果始终复用已有实例。
用静态属性缓存实例(推荐现代写法)
在 class 或构造函数上挂一个静态属性(如 instance),首次执行时创建并保存,后续直接返回它:
- 构造函数内检查
this.constructor.instance是否已存在 - 不存在则赋值
this.constructor.instance = this - 最后统一返回
this.constructor.instance - 注意:不要在原型上设
instance,避免被继承或覆盖
示例:
class Logger {<br> constructor() {<br> if (Logger.instance) {<br> return Logger.instance;<br> }<br> Logger.instance = this;<br> this.logs = [];<br> }<br>}<br><br>const a = new Logger();<br>const b = new Logger();<br>console.log(a === b); // true
用闭包封装私有实例(兼容性好、更安全)
把实例变量放在 IIFE 内部,外部无法访问或篡改,构造函数只作为工厂入口:
当用户要求为 Java 代码添加注释、修改注释、审查注释、整理注释格式,或要求按照代码注释规范处理 Java 文件时,使用本技能。本技能用于统一生成和维护 Java 类注释模板、按 Git 对比 release 判断版本号、补充每行代码上方中文块注释、调整注释空行规则,并保持 Java 文件 UTF-8 无 BOM 编码。典型触发场景包括:补充类注释、更新 @version、检查 @author 来源、整理行尾注释、把注释放到代码上一行、检查注释是否符合规范、构建前检查 Java 注释风格。
- IIFE 返回一个函数,该函数每次调用都检查闭包内的
instance - 首次调用才执行真正的构造逻辑(如
new RealClass()) - 后续调用直接返回缓存的
instance - 完全避开对构造函数的直接 new,天然规避误用
示例:
const Database = (function () {<br> let instance;<br> function init() {<br> return {<br> connect() { console.log('Connected'); },<br> query(sql) { return `Result of ${sql}`; }<br> };<br> }<br> return function () {<br> if (!instance) instance = init();<br> return instance;<br> };<br><br>const db1 = Database();<br>const db2 = Database();<br>console.log(db1 === db2); // true
搭配 getInstance 方法(语义最清晰)
不鼓励直接 new,而是提供统一的静态入口方法。构造函数可设为“约定私有”(如注释说明或抛错提示):
- 类内部用
static instance存储唯一对象 -
static getInstance()是唯一合法获取实例的方式 - 构造函数中可用
if (new.target === Singleton)提示警告(非强制拦截) - 比纯构造函数拦截更易维护、调试和协作
示例:
class Config {<br> static instance;<br> constructor() {<br> if (new.target === Config) {<br> console.warn('Use Config.getInstance() instead of new Config()');<br> }<br> }<br> static getInstance() {<br> if (!this.instance) {<br> this.instance = new Config();<br> this.instance.data = JSON.parse(localStorage.getItem('config') || '{}');<br> }<br> return this.instance;<br> }<br>}
注意事项与常见误区
逻辑单例不是语法锁,需兼顾实用性与健壮性:
- 避免用全局变量缓存实例,模块间可能冲突
- 不要在原型链上挂实例,子类可能意外覆盖
- 带参数的单例要谨慎:首次传参生效,后续忽略(或改用工厂函数支持参数校验)
- 若需销毁或重置,应提供
destroy()方法清理静态引用 - ES2022+ 可用
static #instance私有字段增强封装性
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










