JavaScript构造函数可通过闭包创建真正私有不可变的实例变量,即在函数内用const声明并由内部方法闭包引用,外部无法访问或修改;即使添加同名属性也无效;配合Object.freeze可防止方法被篡改。

JavaScript 中构造函数可以通过闭包在内部创建真正私有的、不可变的实例变量,核心在于利用函数作用域和闭包捕获机制,让变量仅在构造函数执行时被初始化,并对外部完全隐藏。
闭包封装私有数据
每次调用构造函数时,都会创建一个新的词法环境。在该环境中声明的变量(如 const name = 'Alice')会被内部方法通过闭包引用,但外部无法直接访问或修改:
function Person(name, age) {
// 私有不可变变量(闭包内常量)
const _name = name;
const _age = age;
// 公共方法,可读取但不能修改私有变量
this.getName = () => _name;
this.getAge = () => _age;
}
这里 _name 和 _age 是函数局部常量,构造结束后外部既无法读取(无引用路径),也无法赋值(没有 setter,且原始值不可重绑定)。
禁止意外覆盖或篡改
即使尝试在实例上添加同名属性,也不会影响闭包内的值:
立即学习“Java免费学习笔记(深入)”;
在调试 Java 后端 API 故障时使用此技能,尤其适用于以下任务:分析日志、跨 Controller、Service 和 Mapper 层追踪请求、定位异常根因、检查 SQL 执行或参数流转,以及生成简洁的诊断结论与修复方案。
const p = new Person('Bob', 30);
console.log(p.getName()); // 'Bob'
p._name = 'Hacked'; // 无效:只是在实例上新增一个可写属性
console.log(p.getName()); // 仍为 'Bob' —— 闭包中的 _name 未被触碰
这是因为 _name 不是实例属性,而是闭包变量;this._name 并未定义,所以 p._name = ... 创建的是一个全新的、无关的自有属性。
配合 Object.freeze 防止方法被篡改(进阶加固)
若需进一步防止公共方法被替换(如 p.getName = null),可在构造末尾冻结实例:
function Person(name, age) {
const _name = name;
const _age = age;
this.getName = () => _name;
this.getAge = () => _age;
// 冻结实例对象,禁止添加/删除/修改属性(浅冻结)
Object.freeze(this);
}
- Object.freeze(this) 后,this.getName = ... 失败(严格模式报错,非严格模式静默失败)
- 注意:它不冻结闭包变量本身(本就不需要),只保护对象接口的稳定性
- 若返回的对象含嵌套对象,需手动深冻结或使用不可变结构(如 Object.assign({}, ...))
对比原型方式的局限性
如果把 getName 放在 Person.prototype 上,就无法访问闭包变量:
Person.prototype.getName = function() {
return this._name; // ❌ undefined —— _name 不在 this 上,也不在原型链中
};
因此,要使用闭包私有变量,所有访问方法必须在构造函数内定义(即“特权方法”),确保它们能捕获当前执行上下文中的变量。

















