不能只冻结原对象,因为Object.freeze()是浅冻结,仅锁定顶层属性,嵌套对象(如config.features)仍可修改;需先浅拷贝再冻结以隔离引用并确保整体不可变。

直接用 Object.freeze() 冻结原对象,只能防止顶层属性被修改,嵌套对象仍可变。真正构建可靠的只读数据模型,需要先浅拷贝再冻结——既隔离引用,又锁定结构。
为什么不能只 freeze 原对象?
因为 Object.freeze() 是浅冻结:它让对象自身不可新增、删除、重定义属性,但对嵌套的子对象(如 config.features)不递归处理。只要子对象没被冻结,它的属性依然能被改写。
const config = { url: 'a.com', features: { dark: true } };Object.freeze(config);-
config.features.dark = false;→ 成功执行,数据已意外变更
浅拷贝 + freeze 的标准组合流程
先用浅拷贝切断与原始对象的引用关系,再对副本整体冻结。这样既能避免污染源数据,又能确保当前副本不可修改。
- 对普通对象:用
Object.assign({}, origin)或{...origin} - 对数组:用
[...arr]、arr.slice()或Array.from(arr) - 拷贝完成后立即调用
Object.freeze(),返回该冻结后的副本 - 推荐封装成函数复用:
const readOnly = obj => Object.freeze({ ...obj });
只读模型的进阶增强方式
基础组合适合单层或明确只读的简单结构;若需更强保障,可叠加以下策略:
- 多层嵌套只读:手动递归
freeze所有子对象(注意循环引用风险) - 只读+防篡改验证:配合
Object.isFrozen()和Object.getOwnPropertyDescriptor()检查关键字段是否真被锁住 - 只读快照比对:冻结后保留引用,用
===快速判断是否还是同一份快照;内容变化检测则用_.isEqual()等浅比较工具 - 替代方案:用
class封装,所有属性通过get访问器暴露,彻底屏蔽写入入口
常见误区提醒
这个组合看似简单,但几个细节容易出错:
-
const x = Object.freeze({ a: 1 });—— 这只是冻结了对象,x 本身仍可被重新赋值(除非也用const声明) -
Object.freeze()返回的是原对象,不是新对象;所以必须在拷贝后立刻 freeze 拷贝结果 - 冻结后无法撤销,也不影响原型链上的属性;若需动态控制,应考虑 Proxy 方案而非冻结
- IE 不支持
Object.freeze(),旧项目需 polyfill 或降级为防御性只读逻辑

















