Object.freeze、Object.seal 和 Object.preventExtensions 有什么区别?
简化版
preventExtensions 只禁止新增属性;seal 禁止新增和删除属性,并把属性设为不可配置;freeze 在 seal 的基础上让数据属性不可写,是三者中限制最强的。
详细版
三者都只对对象第一层生效:
Object.preventExtensions(obj);
Object.seal(obj);
Object.freeze(obj);
区别:
preventExtensions:不能新增属性,已有属性可改可删。seal:不能新增,不能删除,属性不可配置,但可写属性仍可改值。freeze:不能新增,不能删除,不能改已有数据属性值。
面试重点是限制层级、是否深冻结、严格模式下失败赋值会不会报错。
完整版教学
一、对象属性有多种描述维度
理解这三个 API 前,要先知道对象属性不只有值。
属性还有 writable、configurable、enumerable 等描述符。
这些 API 本质是在批量调整对象的扩展性和属性描述符。
const obj = { a: 1 };
console.log(Object.getOwnPropertyDescriptor(obj, 'a'));
一个普通数据属性通常类似:
value: 1
writable: true
enumerable: true
configurable: true
freeze/seal/preventExtensions 改的不是原型链魔法,而是这些底层开关。
二、preventExtensions 只关掉新增能力
Object.preventExtensions(obj) 会让对象不能再添加新属性。
已有属性仍然可以修改,也可以删除,只要属性本身允许。
它是三者里限制最轻的。
const obj = { a: 1 };
Object.preventExtensions(obj);
obj.a = 2;
delete obj.a;
obj.b = 3;
console.log(obj); // {}
在非严格模式下,新增 b 可能静默失败。
严格模式下,给不可扩展对象新增属性会抛错。
因此面试回答要区分语义限制和运行模式表现。
三、seal 会固定属性集合
Object.seal(obj) 等价于禁止扩展,并把已有自有属性设为不可配置。
不可配置意味着不能删除,也不能重新定义很多描述符。
但如果数据属性仍是可写的,值还可以改。
const obj = { a: 1 };
Object.seal(obj);
obj.a = 2;
delete obj.a;
obj.b = 3;
console.log(obj); // { a: 2 }
流程可以理解为:
seal
-> preventExtensions
-> configurable: false
-> writable 保持原状
这适合“对象结构不能变,但属性值可以更新”的场景。
四、freeze 是最强限制
Object.freeze(obj) 在 seal 的基础上,把已有数据属性设为不可写。
也就是说不能新增、不能删除、不能改值。
它常用于常量配置、防止枚举对象被改写等场景。
const config = Object.freeze({ timeout: 3000 });
config.timeout = 1000;
console.log(config.timeout); // 3000
| API | 可新增 | 可删除 | 可改已有值 | 强度 |
|---|---|---|---|---|
preventExtensions | 否 | 是 | 是 | 低 |
seal | 否 | 否 | 是 | 中 |
freeze | 否 | 否 | 否 | 高 |
如果在严格模式下修改冻结属性,会抛出 TypeError。 这有助于开发期尽早发现错误。
五、它们默认都是浅层的
这三个 API 只处理对象自身第一层属性。 如果属性值还是对象,嵌套对象内部仍然可以修改。 这点非常高频。
const obj = Object.freeze({
nested: { count: 1 }
});
obj.nested.count = 2;
console.log(obj.nested.count); // 2
如果需要深冻结,要递归处理每个嵌套对象。 但深冻结有遍历成本,还要处理循环引用、函数对象等边界。 工程中不应无脑对大对象深冻结。
六、如何检查对象状态
JavaScript 提供了对应检查方法。 它们可以帮助调试,也常出现在笔试题里。
Object.isExtensible(obj);
Object.isSealed(obj);
Object.isFrozen(obj);
记忆钩子:扩展性管“能不能加”,配置性管“能不能删和改描述符”,可写性管“能不能改值”。
如果对象被冻结,通常也是 sealed 和不可扩展的。 但 sealed 对象不一定 frozen,因为属性值可能仍可写。
七、常见误区与追问
- 误区:
freeze会递归冻结所有嵌套对象。 默认只冻结第一层,嵌套对象仍可变。 - 误区:
seal后属性值也不能改。 seal 固定属性集合和配置性,可写属性仍能改值。 - 误区:非严格模式下失败操作一定报错。 很多失败赋值在非严格模式下会静默失败,严格模式才抛错。
- 追问:如何判断对象是否被冻结? 使用
Object.isFrozen(obj)。 - 追问:
freeze和const有什么区别?const固定变量绑定,freeze限制对象属性修改。 - 追问:为什么深冻结要谨慎? 它需要递归遍历对象图,可能遇到循环引用和性能成本。
八、加强记忆
按限制强度记:preventExtensions 不让加,seal 不让加也不让删,freeze 不让加删改。再补浅层限制和严格模式报错差异,就能把这题答完整。