闭包隐藏状态的核心是将私有数据置于函数作用域内,仅通过闭包捕获的方法访问;构造函数或工厂函数均可实现,但原型方法无法访问闭包变量,需避免混用。

在 JavaScript 面向对象编程中,用闭包隐藏状态的核心思路是:把私有数据放在构造函数(或工厂函数)的作用域内,不暴露给实例对象,只通过闭包捕获的函数方法来访问或修改它。
用构造函数 + 闭包实现私有状态
构造函数每次执行都会创建独立的作用域,内部变量无法被外部直接访问,但方法可以借助闭包持续引用这些变量:
- 把需要隐藏的数据(如 balance、_id)声明在构造函数体内,而非挂到 this 上
- 定义的方法(如 deposit、getBalance)在构造函数内声明,自然形成闭包,能读写该作用域的变量
- 这些方法被赋值给 this,成为公有接口;但底层数据始终不可从实例上直接获取
示例:
function BankAccount(initial) {
let balance = initial; // 私有状态,外部不可访问
<p>this.deposit = function(amount) {
if (amount > 0) balance += amount;
};</p><p>this.getBalance = function() {
return balance; // 只能通过方法读取
};
}</p><p>const acc = new BankAccount(100);
console.log(acc.balance); // undefined
console.log(acc.getBalance()); // 100</p>用工厂函数替代构造函数(更自然的闭包模式)
工厂函数不依赖 new,逻辑更清晰,也更符合“闭包封装”的直觉:
- 函数返回一个对象,该对象的方法共享外层函数作用域中的变量
- 没有 this 绑定干扰,私有状态完全由词法作用域保护
- 适合不需要原型继承、强调数据封装的场景
示例:
function createCounter(initial = 0) {
let count = initial;
<p>return {
increment() { count++; },
decrement() { count--; },
value() { return count; }
};
}</p><p>const counter = createCounter(5);
console.log(counter.value()); // 5
console.log(counter.count); // undefined —— 真正私有</p>注意:不能靠命名约定或下划线“假装”私有
JavaScript 没有原生 private 关键字(ES2022 的 # 私有字段是语法级支持,和闭包无关),所以:
- this._internal 或 this.#field(# 是新语法)不是闭包机制,前者可被任意修改,后者虽真私有但不依赖闭包
- 闭包隐藏的关键在于“作用域隔离”,而不是变量名;只要变量没被挂到可访问的对象上,就安全
- 如果误把私有变量赋给 this(如 this.balance = initial),就彻底失去封装性
结合原型时需谨慎:闭包与原型不兼容
想复用方法、节省内存,常会把方法挂在 prototype 上,但这会破坏闭包——原型方法无法访问构造函数内的局部变量:
- 下面写法无效:BankAccount.prototype.getBalance = function() { return balance; }(balance 未定义)
- 若坚持用原型,私有状态只能改用 WeakMap 或 Symbol 键模拟(较复杂),或直接接受 ES2022 的 # 私有字段
- 闭包方案天然适合“每个实例独享方法副本”,对现代 JS 引擎优化影响很小,不必强求原型复用
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











