闭包可实现单例+缓存组合效果:实例仅创建一次且复用,无参时用私有变量保存唯一实例,有参时用map以参数为key缓存不同实例,适用于高成本构造场景。

闭包可以封装私有状态,结合立即执行函数(IIFE)和内部变量,能自然实现单例 + 缓存的组合效果:实例只创建一次,且后续调用直接返回缓存结果。
核心思路:用闭包保存唯一实例和缓存状态
单例的关键是“全局唯一实例”,缓存的关键是“结果复用”。闭包的词法作用域天然支持在函数外部持久保存一个引用,无需依赖全局变量或 class static 属性。
- 定义一个外层函数(常为 IIFE),内部声明一个私有变量(如 instance)用于存储实例
- 返回一个内层函数,它检查 instance 是否已存在;若存在则直接返回,否则创建并赋值
- 若需缓存“初始化参数”对应的不同实例(如按配置生成不同单例),可将 instance 改为 Map 或对象,以参数为 key 存储
基础单例 + 缓存(无参构造)
适用于构造成本高、且无需差异化配置的场景,例如日志器、配置管理器:
const createCachedSingleton = (() => {
let instance = null;
return () => {
if (instance === null) {
instance = { timestamp: Date.now(), id: Math.random() };
console.log('创建新实例');
}
return instance;
};
})();
// 使用
console.log(createCachedSingleton()); // 创建新实例 → { timestamp: ..., id: ... }
console.log(createCachedSingleton()); // 直接返回 → 同一对象(=== true)
带参数的缓存单例(按 key 复用)
当需要根据输入参数(如 API 地址、数据库连接字符串)生成并复用不同单例时,用 Map 实现键控缓存:
const createConfiguredSingleton = (() => {
const cache = new Map();
return (configKey) => {
if (!cache.has(configKey)) {
const instance = {
config: configKey,
createdAt: Date.now(),
data: `instance-for-${configKey}`
};
cache.set(configKey, instance);
console.log(`为 ${configKey} 创建新实例`);
}
return cache.get(configKey);
};
})();
// 使用
console.log(createConfiguredSingleton('db-prod')); // 创建新实例
console.log(createConfiguredSingleton('db-test')); // 创建新实例
console.log(createConfiguredSingleton('db-prod')); // 命中缓存,不重复创建
注意事项与边界情况
闭包单例简洁高效,但需注意以下几点:
- 不可销毁:闭包内的 instance / cache 没有暴露清除接口,适合生命周期与应用一致的对象;如需手动清理,可额外暴露一个 clear 方法
- 非线程安全:JavaScript 单线程下无并发问题,但在 Web Worker 或未来多线程环境需加锁(实际极少需要)
- 避免意外共享:若构造函数返回原始值(如 string、number),会被自动包装,导致 === 判断失效;应确保返回对象引用
- 与模块系统兼容:在 ES Module 中,顶层变量本身就有模块级单例语义,闭包方式更适合作为可复用工具函数,而非替代 export default











