闭包是模拟私有变量最稳妥的方式,因其兼容性好、数据真正不可见;#field 是语法级私有但仅限类内;WeakMap 和 Symbol 均非真正私有,仅作隐藏或折中方案。

用闭包模拟私有变量最稳妥
JavaScript 原生不支持 private 关键字(ES2022 起的 #field 是语法级私有,但仅限类字段),所以闭包仍是封装数据最通用、兼容性最好的方式。核心逻辑是:在函数 作用域 内声明变量,只通过返回的函数暴露有限接口。
function createCounter() { let count = 0; // 外部无法直接访问 return { increment() {count++;}, getValue() { return count;} }; } const counter = createCounter(); counter.increment(); console.log(counter.getValue()); // 3 // console.log(counter.count); // undefined —— 真正不可见
- 适用于任何运行时环境(包括旧版 IE)
- 变量生命周期与闭包绑定,不会被意外修改或垃圾回收过早释放
- 注意避免在循环中创建闭包时捕获错误的引用(常见于
for (let i……) {}之外的写法)
类中的 #field 语法是真私有,但有严格限制
#field 是目前唯一由语言规范保证的私有机制,但仅作用于 class 内部,且不能动态访问(Reflect.get、in、for……in 都无效)。
class BankAccount {#balance = 0; constructor(initial) {this.#balance = initial;} deposit(amount) {this.#balance += amount;} getBalance() { return this.#balance; // ✅ 可读} } const acc = new BankAccount(100); acc.deposit(50); console.log(acc.getBalance()); // 150 // console.log(acc.#balance); // ❌ SyntaxError: Private field '#balance' must be declared in an enclosing class
-
#field必须在类体顶层显式声明(不能在方法里let #x) - 不能用字符串拼接或计算属性名访问:
acc['#' + 'balance']不生效 - 继承时子类无法访问父类的
#field,哪怕同名也不共享
WeakMap 实现“伪私有”对象级封装
当需要为已有构造函数或原型方法添加私有状态,又不想改结构时,WeakMap 是折中选择:以实例为 key,存储独立数据对象,避免内存泄漏。
const privateData = new WeakMap(); class Logger { constructor(name) {privateData.set(this, { name, level: 'info'}); } getName() { return privateData.get(this).name; } setLevel(level) {privateData.get(this).level = level; } }
- 适合装饰已有类或需动态挂载私有状态的场景
- 必须确保每次调用
privateData.get(this)前已用set初始化,否则返回undefined - 无法阻止用户手动调用
privateData.get(instance)—— 它不是语言级私有,只是“不易发现”
Symbol 作为属性键只能防误读,不算真正私有
Symbol 创建的属性名不会出现在 for……in 或 Object.keys() 中,但可通过 Object.getOwnPropertySymbols() 暴露,属于“隐藏而非禁止”。
立即学习“Java 免费学习笔记(深入)”;
const _id = Symbol('id'); class User {constructor(id) {this[_id] = id; } getId() { return this[_id]; } } const u = new User(123); console.log(u[_id]); // 123 —— 可直接访问 console.log(Object.getOwnPropertySymbols(u)); // [Symbol(id)]
- 适合标记内部用途属性,比如避免与用户传入的 key 冲突
- 完全不提供访问控制,仅降低被意外遍历到的概率
- 不能用于 敏感数据 保护场景
闭包和 #field 是唯二能真正隔离数据的方案;WeakMap 和 Symbol 都依赖约定或运行时特性,一旦脱离上下文就失去“私有”意义。选哪种,取决于你是否需要跨 浏览器 支持、是否接受类语法约束、以及对“私有”的定义到底有多严格。






























