在JavaScript开发中,对象默认是可变的,我们可以随意添加、修改、删除对象的属性,这种特性在某些场景下会引发意外的数据篡改问题。为了避免这种情况,JS提供了多种对象冻结方法,帮助开发者创建不可变对象,保障数据的稳定性。

JS常用的对象冻结方法
JS内置了三个用于限制对象修改的方法,它们的限制程度从低到高依次是Object.preventExtensions、Object.seal、Object.freeze,下面分别介绍每个方法的特性。
1. Object.preventExtensions
这个方法的作用是禁止对象添加新的属性,但是已有的属性可以被修改或者删除。
// 创建一个普通对象
const obj1 = { name: '张三', age: 20 };
// 禁止对象扩展
Object.preventExtensions(obj1);
// 尝试添加新属性,严格模式下会报错,非严格模式下静默失败
obj1.gender = '男';
console.log(obj1.gender); // undefined
// 可以修改已有属性
obj1.age = 21;
console.log(obj1.age); // 21
// 可以删除已有属性
delete obj1.name;
console.log(obj1.name); // undefined
// 判断对象是否不可扩展
console.log(Object.isExtensible(obj1)); // false
2. Object.seal
这个方法在Object.preventExtensions的基础上,进一步禁止删除对象的已有属性,但是已有属性的值仍然可以被修改。
// 创建一个普通对象
const obj2 = { name: '李四', age: 25 };
// 密封对象
Object.seal(obj2);
// 尝试添加新属性,静默失败
obj2.gender = '女';
console.log(obj2.gender); // undefined
// 尝试删除已有属性,静默失败
delete obj2.name;
console.log(obj2.name); // 李四
// 可以修改已有属性
obj2.age = 26;
console.log(obj2.age); // 26
// 判断对象是否被密封
console.log(Object.isSealed(obj2)); // true
3. Object.freeze
这是限制程度最高的方法,它会完全冻结对象:不能添加新属性、不能删除已有属性、不能修改已有属性的值,同时对象本身也不能被修改原型。
// 创建一个普通对象
const obj3 = { name: '王五', age: 30 };
// 冻结对象
Object.freeze(obj3);
// 尝试添加新属性,静默失败
obj3.gender = '男';
console.log(obj3.gender); // undefined
// 尝试删除已有属性,静默失败
delete obj3.name;
console.log(obj3.name); // 王五
// 尝试修改已有属性,静默失败
obj3.age = 31;
console.log(obj3.age); // 30
// 判断对象是否被冻结
console.log(Object.isFrozen(obj3)); // true
浅冻结与深冻结的区别
需要注意的是,Object.freeze默认是浅冻结,也就是说如果对象的属性值本身是对象或者数组,那么这个属性值内部的内容还是可以被修改的。
const obj4 = {
name: '赵六',
hobbies: ['吃饭', '睡觉']
};
// 浅冻结对象
Object.freeze(obj4);
// 修改基本类型属性,静默失败
obj4.name = '钱七';
console.log(obj4.name); // 赵六
// 修改引用类型属性的内部内容,修改成功
obj4.hobbies.push('打游戏');
console.log(obj4.hobbies); // ['吃饭', '睡觉', '打游戏']
如果需要实现深冻结,也就是让对象所有层级的属性都不可修改,就需要递归遍历对象的所有属性,对每个属性值如果是对象或数组的话也执行Object.freeze。
// 深冻结函数
function deepFreeze(target) {
// 获取对象所有自身属性的属性名
const propNames = Object.getOwnPropertyNames(target);
// 先冻结自身
Object.freeze(target);
// 遍历所有属性
for (const name of propNames) {
const value = target[name];
// 如果属性值是对象或者数组,且不为null,就递归冻结
if (value && typeof value === 'object') {
deepFreeze(value);
}
}
return target;
}
const obj5 = {
name: '孙八',
hobbies: ['看书', '跑步'],
info: { address: '北京' }
};
// 深冻结对象
deepFreeze(obj5);
// 修改引用类型属性的内部内容,静默失败
obj5.hobbies.push('游泳');
console.log(obj5.hobbies); // ['看书', '跑步']
obj5.info.address = '上海';
console.log(obj5.info.address); // 北京
三种方法的对比
为了更清晰地了解三个方法的区别,我们可以通过下面的表格对比它们的特性:
| 方法 | 禁止添加属性 | 禁止删除属性 | 禁止修改属性值 | 影响层级 |
|---|---|---|---|---|
| Object.preventExtensions | 是 | 否 | 否 | 仅当前对象 |
| Object.seal | 是 | 是 | 否 | 仅当前对象 |
| Object.freeze(浅) | 是 | 是 | 是 | 仅当前对象的第一层属性 |
| Object.freeze(深,递归实现) | 是 | 是 | 是 | 对象所有层级的属性 |
使用场景建议
在实际开发中,我们可以根据需求选择合适的方法:
- 如果只需要禁止对象添加新属性,允许修改和删除已有属性,使用
Object.preventExtensions - 如果需要禁止添加和删除属性,但是允许修改已有属性的值,使用
Object.seal - 如果需要完全冻结对象的第一层属性,使用
Object.freeze - 如果对象包含多层嵌套的引用类型属性,需要所有层级都不可修改,就使用递归实现的深冻结方法
需要注意的是,这些冻结方法在严格模式下,对对象的违规操作会直接抛出错误,非严格模式下只会静默失败,所以建议在开发时开启严格模式,方便及时发现问题。
Object_freezeObject_sealObject_preventExtensions不可变对象修改时间:2026-06-23 15:03:36