const声明的对象引用不可变但属性可修改,因其锁定内存地址而非对象内容;如需完全冻结需用object.freeze()或深度冻结。

用 const 声明对象时,“引用不变”是指变量绑定的内存地址不能变;“属性可变”是指该地址指向的对象内部数据可以修改。关键不在“能不能改”,而在于“改的是什么”。
引用不变:地址锁死,换不了“家”
const 保证的是变量名始终指向同一个堆内存地址。一旦声明,就不能把另一个新对象赋给它。
-
✅ 合法操作:
const obj = { a: 1 };—— 地址已固定 -
❌ 报错操作:
obj = { b: 2 };—— 试图换地址,触发 Assignment to constant variable -
✅ 合法操作:
obj.a = 10;或obj.newProp = 'ok';—— 还在原地址里操作
属性可变:对象内部是“开放房间”
对象本身存放在堆内存中,const 并不冻结它的结构或内容。只要没动“指向它的那个指针”,任何属性增删改都允许。
- 可以添加属性:
obj.id = 123; - 可以修改已有值:
obj.a = 'hello'; - 可以删除属性:
delete obj.a; - 数组同理:
const arr = [1, 2]; arr.push(3); arr[0] = 99;都合法
想真正冻结对象?得加点料
如果需要让对象内容也不许变,必须显式调用 Object.freeze()。它只冻结当前层级,不递归:
const user = Object.freeze({ name: 'Tom', info: { age: 30 } });-
user.name = 'Jack';→ 无效(静默失败,严格模式下报错) -
user.info.age = 31;→ 仍然生效(info对象未被冻结) - 如需深度冻结,需手动递归调用
Object.freeze或使用第三方库
为什么设计成这样?不是更混乱吗?
这种设计兼顾了安全与实用:
- 防止意外重赋值(比如误写
config = newConfig覆盖整个配置) - 又保留运行时灵活更新状态的能力(比如组件中的
const state = { count: 0 },后续state.count++很自然) - 底层机制统一:所有引用类型(对象、数组、函数、正则等)都遵循“地址恒定、内容自由”原则










