不可修改对象的三种方法及差异与应用场景
基本概念与定位
对象不可修改并不等同于不可变性,三种方法提供了不同等级的结构与数据改动限制,帮助开发者控制对象的行为。
Object.preventExtensions() 仅阻止向对象添加新属性,原有属性仍然可以被修改或删除,这是最小的限制级别。
Object.seal() 在防止扩展的基础上,进一步使所有属性的 可配置性不可更改,且不能新增属性,但对已有属性的可写性取决于其初始属性特性。
Object.freeze() 将对象变为几乎不可变:所有现有属性不可写、不可配置,不能新增也不能删除,但这是“浅冻结”,嵌套对象需要递归处理才能实现真正的深层不可变性。
Object.preventExtensions():防止对象扩展
核心行为与限制
核心作用是阻止新增属性,不会改变现有属性的写入或删除权限,因而原有属性仍然可以被修改或删除。
应用场景:你希望在某些对象上限定结构不可扩展,但又允许对现有字段进行调整,例如在数据传输对象(DTO)层提高约束性,而不强制冻结每个字段。
使用要点:该方法属于“最小约束级别”,适合在运行时阶段临时控制对象扩展性。请注意,在严格模式下尝试新增属性通常会抛出 TypeError。
// 防止扩展的示例(严格模式下)"use strict";const obj = {a:1};Object.preventExtensions(obj);obj.b = 2; // TypeError: Cannot add property 'b', object is not extensible
示例扩展行为:即使不能新增属性,仍可以删除现有属性或修改其值(取决于属性的可写性)。
const o = {a:1, b:2};Object.preventExtensions(o);delete o.a; // true,能够删除现有属性o.b = 42; // true,仍然可以修改现有属性的值console.log(o); // { b: 42 }Object.seal():对对象进行密封
核心行为与限制
密封对象不仅阻止新增属性,还不可删除现有属性,并且将所有现有属性的 可配置性设为不可配置,使得对属性的结构性修改更受限。
应用场景:你需要锁定对象的结构,避免未来的变更(增删属性),同时允许对已有可写属性的值进行更新,常用于稳定配置对象或模块边界契约。

使用要点:在密封对象后,新增属性被拒绝,删除属性也会失效,但前提是现有属性仍然可写,否则无法修改其值。
let s = {x:1, y:2};
Object.seal(s);
s.z = 3; // 不会新增属性,静默失效
delete s.x; // false,不能删除现有属性
s.x = 9; // 仍然可以修改已有属性的值(若该属性可写)
console.log(s); // { x: 9, y: 2 }
Object.freeze():冻结对象
核心行为与限制
冻结对象是最强的不可变性锁定:所有现有属性不可写、不可配置,并且无法新增或删除属性。
应用场景:需要确保对象在整个生命周期内不被改动的场景,如保护配置、跨模块的不可变状态、或避免意外修改数据模型。
使用要点:这是一个浅冻结,若对象属性包含引用类型的嵌套对象,嵌套对象仍可能可变,需要递归冻结才能实现真正的深层不可变。
let f = {a:1, b:{c:2}};
Object.freeze(f);
f.a = 9; // 无效,属性不可写
delete f.a; // 无效,属性不可删除
f.b.c = 3; // 仍然可修改嵌套对象中的值(浅冻结特性)
console.log(f); // { a: 1, b: { c: 3 } }
// 深度冻结示例(递归实现)function deepFreeze(obj) {Object.freeze(obj);Object.getOwnPropertyNames(obj).forEach(function(prop) {const value = obj[prop];if (value && typeof value === 'object' && !Object.isFrozen(value)) {deepFreeze(value);}});return obj;}const data = { a: 1, b: { c: 2 } };deepFreeze(data);data.b.c = 5; // 不会生效,深冻结后嵌套对象也被冻结三者对比与应用要点
对比要点与选择要点
关键差异点在于:preventExtensions 仅阻止新增属性、seal 增强到不可新增不可删除且不可配置、freeze 则进一步不可写且不可配置,且是浅冻结,这就是它们在实际场景中的核心区分。
深度不可变性需自行实现:无论是 seal 还是 freeze,嵌套对象默认是可变的,需要通过 手工递归冻结(如 deepFreeze 实现)来达到真正的深层不可变性。
对性能的潜在影响:对对象应用冻结会影响后续对该对象的修改检测和属性描述的写入行为,开发中应权衡是否需要频繁修改对象结构。
在模块边界与数据契约中常见使用场景:当你需要确保公共配置、默认参数、或模块间通信数据的结构稳定时,可以选择合适的方法来限制对象的变动范围。


