es modules 中通过工厂函数导出封装私有状态的实例,利用闭包隐藏内部变量;可返回普通对象、类构造器或单例,确保私有性与实例隔离。

在 ES Modules 中,无法直接“导出私有实例”,但可以通过导出一个函数(工厂函数),在调用时创建并返回封装了私有状态的实例。这种模式利用了闭包来隐藏内部变量,是实现模块级私有性的标准方式。
用工厂函数封装私有状态
导出一个函数,该函数内部定义变量和方法,只暴露需要对外使用的接口,内部变量自然成为闭包中的私有数据:
// counter.js
export function createCounter(initial = 0) {
let count = initial; // 私有变量,外部不可直接访问
return {
increment() {
count++;
return count;
},
decrement() {
count--;
return count;
},
get() {
return count;
}
};
}
使用时:
// main.js
import { createCounter } from './counter.js';
const c1 = createCounter(10);
console.log(c1.get()); // 10
c1.increment();
console.log(c1.get()); // 11
const c2 = createCounter(100); // 独立私有状态
console.log(c2.get()); // 100
返回类实例(仍保持私有性)
如果偏好 class 语法,可将类定义在工厂函数内部,确保每个实例都拥有独立的闭包作用域:
// user.js
export function createUser(name, email) {
// 私有字段(仅在此闭包中可访问)
const _name = name;
const _email = email;
let _loginCount = 0;
return class User {
getName() {
return _name;
}
getEmail() {
return _email;
}
login() {
_loginCount++;
return _loginCount;
}
};
}
注意:这里返回的是类本身(不是实例),调用方需 new:
import { createUser } from './user.js';
const UserClass = createUser('Alice', 'a@example.com');
const user = new UserClass(); // 实例共享该闭包中的 _name/_email/_loginCount
console.log(user.getName()); // 'Alice'
导出单例(带私有状态)
若只需一个全局实例且希望其状态私有,可直接导出工厂调用结果:
// config.js
const _config = {
apiUrl: 'https://api.example.com',
timeout: 5000,
debug: false
};
export const config = {
get(key) {
return _config[key];
},
set(key, value) {
if (key in _config) {
_config[key] = value;
}
}
};
这样 _config 对象不会被外部修改或访问,仅通过 config 提供的接口操作。
避免常见误区
- 不要试图用
#私有字段 + 默认导出 class 来“隐藏实例”——ESM 导出的是绑定,不是封装;私有字段只限制属性访问,不阻止对象被导出或复制。 - 不要把私有变量挂在模块顶层再导出对象引用——那样所有导入者共享同一份状态,且变量可被意外修改(除非用
const+ 不可变结构)。 - 工厂函数每次调用生成新闭包,确保多个实例间状态隔离。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











