object.freeze 是浅冻结,只冻结对象自身可枚举属性,不递归冻结嵌套对象;深度冻结需手动递归判断并冻结普通对象和数组,跳过函数、date、regexp等不可冻结类型。

Object.freeze 只冻结对象自身的可枚举属性,不会递归冻结嵌套对象——要实现深度冻结,必须手动遍历并冻结所有层级的普通对象。
为什么 Object.freeze 不是深度冻结
JavaScript 的 Object.freeze 是浅冻结:它阻止添加、删除、修改自有属性,但对属性值为对象的情况不做处理。如果某个属性值本身是对象,该对象仍可被修改。
- 冻结后,
obj.prop = newValue失败(静默或报错,取决于严格模式) - 但若
obj.nested是对象,则obj.nested.field = 123依然成功 - 数组同理:
Object.freeze([{}])后,arr[0].x = 1仍可执行
手动实现深度冻结的核心逻辑
深度冻结需递归检测每个属性值:如果是普通对象(非 null、非原始类型、非 Date/RegExp/ArrayBuffer 等内置类型),就对其调用 Object.freeze,再递归处理其自身属性。
- 先用
Object.isFrozen(obj)避免重复冻结 - 用
Object.prototype.toString.call(value) === '[object Object]'或更稳妥的value && typeof value === 'object' && !Array.isArray(value) && !(value instanceof Date) && !(value instanceof RegExp)判断是否为“可冻结的普通对象” - 遍历自有可枚举属性(
Object.keys(obj)),对符合条件的值递归冻结 - 最后调用 Object.freeze(obj) 冻结当前层
一个简洁可靠的深度冻结函数
以下函数兼顾安全性与实用性,跳过不可冻结类型(如函数、日期、正则、基本包装对象),仅冻结 plain object 和数组:
function deepFreeze(obj) {
if (obj == null || typeof obj !== 'object') return obj;
if (Object.isFrozen(obj)) return obj;
// 冻结数组和普通对象
if (Array.isArray(obj) || obj.constructor === Object) {
Object.keys(obj).forEach(prop => {
deepFreeze(obj[prop]);
});
}
return Object.freeze(obj);
}
注意:该函数不处理 Map、Set、TypedArray 等复杂结构,实际项目中需按需扩展。
使用时的注意事项
深度冻结是不可逆操作,且会影响性能和内存——尤其对大型嵌套结构。
- 冻结前确保对象已构造完成;冻结后无法再添加响应式属性(影响 Vue/React 等框架的 reactivity)
- 冻结后的对象仍可通过
Object.assign({}, frozenObj)创建可变副本,但副本不继承冻结状态 - 调试时可用
console.log(Object.isFrozen(obj), Object.isFrozen(obj.nested))验证各层是否生效
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











