推荐使用静态 getter + 私有静态字段实现真正单例,首次访问时延迟初始化并全局共享;支持 init() 配置、类型推导和线程安全,比实例 getter 更符合单例语义。

在类内部用 getter 实现按需延迟加载的轻量单例,核心是:不预先创建实例,首次访问时才初始化,并缓存结果供后续复用。它比传统静态私有实例 + 静态方法更简洁、更符合封装习惯,且天然线程安全(JavaScript 单线程环境下无需额外同步)。
getter 内部缓存实例
在类的实例属性上用 私有字段 存储已创建的实例,getter 检查该字段是否为 null/undefined,是则新建并赋值,否则直接返回缓存值。
例如:
class ConfigLoader {
#instance = null;
<p>get instance() {
if (!this.#instance) {
this.#instance = new ConfigLoader();
}
return this.#instance;
}</p><p>constructor() {
// 避免重复初始化逻辑(如读取文件、解析 JSON)
if (this.#instance) return;
console.log('Config loaded');
}
}</p>⚠️ 注意:上面写法会让 new ConfigLoader() 创建新实例,而 obj.instance 返回另一个——这不是单例。正确做法是让构造函数禁止外部调用,或改用静态 getter。
推荐:静态 getter + 私有静态字段(真正单例)
单例应全局唯一,适合用 静态 getter 管理,配合私有静态字段缓存。这样无论从哪个实例或直接调用,都共享同一份实例。
- 用
static #instance = null声明私有静态缓存 - 用
static get instance()提供访问入口 - 构造函数内不做实际初始化,把初始化逻辑移到 getter 中
class Database {
static #instance = null;
<p>static get instance() {
if (!this.#instance) {
this.#instance = new Database();
// ✅ 延迟执行初始化(连接、校验等)
this.#instance.#connect();
}
return this.#instance;
}</p><h1>connection = null;</h1><h1>connect() {</h1><pre class="brush:php;toolbar:false;"><pre class="brush:php;toolbar:false;">console.log('Connecting to DB...');
this.#connection = { status: 'ready' };}
query(sql) {
return Executed: ${sql}
// 使用 console.log(Database.instance.query('SELECT ')); // Connecting to DB... → Executed: SELECT console.log(Database.instance.query('UPDATE')); // 直接执行 → Executed: UPDATE
支持初始化参数的变体(带配置的延迟单例)
若单例需接收初始化参数(如 API 地址、超时时间),不能直接在静态 getter 里传参。可行方案是:
- 提供一个
init(config)静态方法,首次调用时设置配置并创建实例 - getter 内检查是否已 init,未 init 则抛错或返回默认行为
- 或使用“惰性初始化 + 参数记忆”模式:允许首次
instance访问时传参(需搭配私有静态标记)
简单安全的做法:
class HttpClient {
static #instance = null;
static #config = null;
<p>static init(config) {
if (this.#instance) throw new Error('Already initialized');
this.#config = { timeout: 5000, ...config };
this.#instance = new HttpClient();
}</p><p>static get instance() {
if (!this.#instance) {
throw new Error('Call HttpClient.init() first');
}
return this.#instance;
}</p><p>request(url) {
return <code>GET ${url} with timeout ${HttpClient.#config.timeout}</code>;
}
}</p>与传统单例对比优势
相比手动维护 getInstance() 方法,静态 getter 方式更自然:
- 语法简洁:
Database.instance比Database.getInstance()少打 5 个字符 - 语义清晰:
instance是一个“属性”,而非“动作”,符合按需获取的直觉 - 天然不可写:只要不暴露 setter,外部无法篡改单例引用
- 支持 TypeScript 类型推导,IDE 可自动补全实例方法
不复杂但容易忽略:确保初始化逻辑只执行一次,且不依赖外部可变状态;私有字段(#)是现代 JS 的关键支撑,避免污染原型或暴露实现细节。











