JavaScript中const仅保证绑定不可变,不保证值不可变;基本类型因值传递等效不可变,引用类型需Object.freeze()或结构复制实现浅层不可变,深层不可变推荐immer等工具。
JavaScript 本身没有原生的“不可变类型”概念,const 只是阻止变量重新赋值,不保证值不可变;对象和数组默认都是可变的。理解不可变性,关键在于区分「引用不可变」和「值不可变」。
const 不等于不可变?const 声明的只是绑定不可变:变量不能指向新地址,但若它指向的是对象,对象内部属性仍可修改。
const user = { name: 'Alice' };
user.name = 'Bob'; // ✅ 合法:对象内容被修改
user = { name: 'Charlie' }; // ❌ 报错:Canno
t 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() 能否真正实现不可变?它只能做浅冻结:顶层属性不可增删改,但嵌套对象仍可变。
立即学习“Java免费学习笔记(深入)”;
const obj = { a: 1, nested: { b: 2 } };
Object.freeze(obj);
obj.a = 3; // ❌ 无效
obj.nested.b = 4; // ✅ 仍然生效!
Object.freeze()(注意循环引用会爆栈)immer 写“看似可变”的代码,底层生成新对象不追求绝对冻结,而是在关键数据流节点主动创建新值。重点不是“防住所有修改”,而是“让修改可预测、可追踪、可回退”。
.push()、.splice()、.assign() 等就地方法[...arr, newItem]、arr.filter()、arr.map() 等返回新数组的方法immer:写法自然,自动保障不可变语义,且支持异步、Proxy、Draft 模式Map 和 Set:它们的方法(如 set()、add())仍是就地修改,需手动包装成返回新实例的函数真正的难点不在语法层面,而在于团队对“何时必须新建、何时可以复用”的共识。比如一个临时计算用的 filter 结果,没必要深克隆;但全局用户配置对象的每次更新,必须切断引用链。这个边界,比 const 或 freeze 更值得花时间定义。