object.freeze 只浅冻结对象,不递归冻结嵌套属性;深度冻结需手动递归处理普通对象和数组,跳过原始类型及不可冻结内置对象,并先冻结子属性再冻结父对象。

Object.freeze 只能浅冻结对象,无法直接深度冻结嵌套对象或数组。要实现“深度冻结”,需手动递归处理所有嵌套的可冻结值(如普通对象、数组),跳过原始类型(如字符串、数字、null)和不可冻结对象(如 Date、RegExp、函数等)。
为什么 Object.freeze 不是深度的?
它只冻结目标对象自身的属性,不递归冻结属性值。例如:
const obj = { a: 1, nested: { b: 2 } };
Object.freeze(obj);
obj.nested.b = 3; // ✅ 仍可修改!
如何实现深度冻结?
写一个递归函数,对每个属性值判断类型,仅对 plain object 和 array 进行 freeze 并继续递归:
- 用
Object.prototype.toString.call(value) === '[object Object]'或value !== null && typeof value === 'object' && !Array.isArray(value)判断是否为普通对象 - 用
Array.isArray(value)判断是否为数组 - 跳过原始类型(string/number/boolean/null/undefined/symbol/bigint)、函数、Date、RegExp、Map、Set 等内置对象(它们不可安全 freeze 或无意义)
- 先递归冻结子属性,再调用
Object.freeze(target)—— 保证子对象已冻结,避免被中途修改
一个实用的 deepFreeze 函数示例
支持对象和数组,忽略不可冻结值:
function deepFreeze(obj) {
if (obj === null || typeof obj !== 'object') return obj;
// 跳过函数、日期、正则、内置对象等
if (typeof obj === 'function' || obj instanceof Date || obj instanceof RegExp) return obj;
if (obj instanceof Map || obj instanceof Set || obj instanceof WeakMap || obj instanceof WeakSet) return obj;
// 先冻结所有可遍历属性(包括数组元素)
Object.getOwnPropertyNames(obj).forEach(prop => {
deepFreeze(obj[prop]);
});
return Object.freeze(obj);
}
// 使用示例
const data = {
name: 'Alice',
scores: [95, 87],
meta: { version: 2, tags: ['js', 'immutable'] }
};
deepFreeze(data);
data.meta.tags.push('frozen'); // ❌ TypeError
注意事项与限制
- 循环引用会导致无限递归,使用 WeakMap 缓存已处理对象可解决(进阶需求)
- freeze 后无法添加、删除、修改属性,也无法改变属性描述符;但不影响原型链上的属性
- Symbol 属性也会被冻结(Object.getOwnPropertyNames 不返回 Symbol,需用 Object.getOwnPropertySymbols 补充)
- 对于 class 实例,freeze 仅作用于自有属性,不影响原型方法调用,也不冻结内部状态(如 #private 字段逻辑上不受影响)
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











