Object.freeze仅浅冻结数组本身,阻止增删改元素和length,但不冻结内部对象属性;需递归调用deepFreeze或使用Immer等库实现深层不可变。

Object.freeze 可以冻结数组本身(防止增删改索引、修改 length),但不会递归冻结数组中的对象元素。也就是说,数组的结构被锁住,但若数组里存的是对象,这些对象内部属性仍可被修改。
冻结数组本身:阻止结构变化
调用 Object.freeze(arr) 后:
- 不能新增、删除或替换数组元素(如
arr.push()、arr[0] = ...、arr.length = 0都会静默失败(非严格模式)或报错(严格模式)) - 不能修改
length属性 - 数组仍是“类数组”对象,
Array.isArray()返回true
✅ 示例:
const arr = [{ name: 'Alice' }, { name: 'Bob' }];
Object.freeze(arr);
arr[0] = { name: 'Charlie' }; // ❌ 无效(静默失败)
arr.push({ name: 'Dave' }); // ❌ 无效
console.log(arr); // [{ name: 'Alice' }, { name: 'Bob' }]
注意:对象元素内部仍可变
虽然数组被冻结,但其中的对象未被冻结:
arr[0].name = 'Alex'; // ✅ 成功!对象属性仍可修改 console.log(arr[0].name); // 'Alex'
这是因为 Object.freeze 是浅冻结(shallow freeze)——只作用于目标对象自身的属性,不深入嵌套对象。
实现真正不可变:递归冻结(含数组与对象)
若需确保整个数据结构(包括嵌套对象、数组)不可变,需手动递归冻结:
function deepFreeze(obj) {
if (obj === null || typeof obj !== 'object') return obj;
// 先冻结自身
Object.freeze(obj);
// 遍历所有属性,递归冻结(跳过 Symbol 和不可枚举属性)
Object.getOwnPropertyNames(obj).forEach(prop => {
if (obj[prop] !== null && typeof obj[prop] === 'object') {
deepFreeze(obj[prop]);
}
});
return obj;
}
const arr = [{ name: 'Alice', tags: ['user'] }, { name: 'Bob' }];
deepFreeze(arr);
arr[0].name = 'Eve'; // ❌ 失败(对象已被冻结)
arr[0].tags.push('admin'); // ❌ 失败(子数组也被冻结)
替代方案:使用不可变数据结构库
生产环境建议用成熟方案处理深层不可变性:
- immer:允许用“可变语法”写逻辑,底层生成新不可变对象
- immutable-js(较重,API 不同)
- ES2023+ 的
Object.hasOwn()等辅助方法配合手动冻结
单纯靠 Object.freeze 适合简单场景(如配置常量数组、纯原始值数组),复杂嵌套结构请勿依赖它保证深层不可变。

















