标准答案
- 可变对象可以在原引用上直接修改属性。不可变思路强调不直接改原对象,而是创建新对象表达变化。
- 前端状态管理里,不可变更新更容易做引用比较、变更追踪、撤销回滚和缓存判断,也能减少多个地方共享同一个对象时的意外修改。
- Object.preventExtensions 禁止新增属性,但已有属性仍然可以修改或删除。
- Object.seal 在 preventExtensions 的基础上禁止删除属性,并把已有属性设为不可配置。
- Object.freeze 再进一步把数据属性设为不可写,通常表示浅冻结。它不会自动冻结嵌套对象。
题目解析
不可变不是说所有数据都必须深拷贝。常见做法是只复制发生变化的路径,其它未变化分支继续共享引用,这样既能保留引用比较能力,也不会无意义复制整棵对象。
freeze、seal、preventExtensions 都是浅层限制。嵌套对象如果仍然是普通可变对象,仍然可以被修改。需要深冻结时要递归处理,但工程上更常见的是用不可变更新约定或状态管理工具。
严格模式下修改冻结对象的只读属性会报错;非严格模式下可能静默失败。面试里要能说出这类差异。
代码示例
下面示例展示不可变更新、浅冻结和三个对象限制 API 的差异。
JavaScript
const state = {
user: {
name: 'Alice',
profile: { city: 'Hangzhou' }
}
}
const nextState = {
...state,
user: {
...state.user,
name: 'Bob'
}
}
nextState !== state // true
nextState.user !== state.user // true
nextState.user.profile === state.user.profile // true,未变分支共享引用
const frozen = Object.freeze({
name: 'Alice',
profile: { city: 'Hangzhou' }
})
frozen.profile.city = 'Shanghai' // 嵌套对象仍然可改
const sealed = Object.seal({ name: 'Alice' })
sealed.name = 'Bob'
// sealed.age = 18 // 不能新增
// delete sealed.name // 不能删除
const fixedShape = Object.preventExtensions({ name: 'Alice' })
fixedShape.name = 'Bob'
// fixedShape.age = 18 // 不能新增常见误区
- 误以为 Object.freeze 是深冻结。
- 把不可变更新理解成必须深拷贝整个对象。
- 忽略数组也是对象,也可以被冻结或被不可变更新。