JavaScript中const仅保证绑定不可变,不保证值不可变;基本类型因值传递等效不可变,引用类型需Object.freeze()或结构复制实现浅层不可变,深层不可变推荐immer等工具。

JavaScript 本身没有原生的“不可变类型”概念,const 只是阻止变量重新赋值,不保证值不可变;对象和数组默认都是可变的。理解不可变性,关键在于区分「引用不可变」和「值不可变」。
为什么 const 不等于不可变?
const 声明的只是绑定不可变:变量不能指向新地址,但若它指向的是对象,对象内部属性仍可修改。
const user = { name: 'Alice' };
user.name = 'Bob'; // ✅ 合法:对象内容被修改
user = { name: 'Charlie' }; // ❌ 报错:Cannot assign to const variable
-
const对基本类型(string、number、boolean等)效果等同于不可变,因为它们是值传递 -
const对引用类型(Object、Array、Date等)只冻结“指针”,不冻结“内存里的内容” - 真正实现值不可变,需用
Object.freeze()或结构复制(如展开运算符、structuredClone())
如何安全地更新嵌套对象而不改变原对象?
直接修改嵌套属性(如 obj.user.profile.age = 30)会污染原始数据,尤其在 React、Redux 或函数式编程中易引发意外副作用。
- 浅层对象用展开运算符:
{ ...obj, field: newValue } - 深层嵌套推荐用函数式工具(如
immer的produce),或手动分层展开 - 避免滥用
JSON.parse(JSON.stringify(obj)):丢失函数、undefined、Date、RegExp等
const original = { user: { profile: { name: 'Alice', age: 25 } } };
// ✅ 安全更新 age
const updated = {
...original,
user: {
...original.user,
profile: {
...original.user.profile,
age: 26
}
}
};
Object.freeze() 能否真正实现不可变?
它只能做浅冻结:顶层属性不可增删改,但嵌套对象仍可变。
本书是全面讲述PHP与MySQL的经典之作,书中不但全面介绍了两种技术的核心特性,还讲解了如何高效地结合这两种技术构建健壮的数据驱动的应用程序。本书涵盖了两种技术新版本中出现的最新特性,书中大量实际的示例和深入的分析均来自于作者在这方面多年的专业经验,可用于解决开发者在实际中所面临的各种挑战。
const obj = { a: 1, nested: { b: 2 } };
Object.freeze(obj);
obj.a = 3; // ❌ 无效
obj.nested.b = 4; // ✅ 仍然生效!
- 要深冻结,需递归调用
Object.freeze()(注意循环引用会爆栈) - 生产环境一般不用深冻结——性能差、无法撤销、与大多数库(如 React DevTools)不兼容
- 更实用的做法是约定 + 工具:用
immer写“看似可变”的代码,底层生成新对象
现代 JS 中推荐的不可变实践路径
不追求绝对冻结,而是在关键数据流节点主动创建新值。重点不是“防住所有修改”,而是“让修改可预测、可追踪、可回退”。
- 状态更新一律返回新对象/数组,禁用
.push()、.splice()、.assign()等就地方法 - 优先使用
[...arr, newItem]、arr.filter()、arr.map()等返回新数组的方法 - 复杂场景引入
immer:写法自然,自动保障不可变语义,且支持异步、Proxy、Draft 模式 - 注意
Map和Set:它们的方法(如set()、add())仍是就地修改,需手动包装成返回新实例的函数
真正的难点不在语法层面,而在于团队对“何时必须新建、何时可以复用”的共识。比如一个临时计算用的 filter 结果,没必要深克隆;但全局用户配置对象的每次更新,必须切断引用链。这个边界,比 const 或 freeze 更值得花时间定义。









