object.isfrozen(obj)返回true需同时满足:对象不可扩展、所有自身属性不可配置、所有自身数据属性不可写;它不递归检查嵌套对象,对原始值和null也返回true。

Object.isFrozen 返回 true 的真实条件是什么
Object.isFrozen 只有在对象本身不可扩展(Object.isExtensible(obj) === false),且所有自有属性都为不可配置、不可写时,才返回 true。它不检查原型链上的属性,也不关心属性值是否是引用类型——哪怕某个属性值是另一个可变对象,只要该属性本身被设为 writable: false 且 configurable: false,就不影响判定。
常见误判场景:
- 用
Object.freeze({ a: {} })冻结后,obj.a.push(1)看似“生效”,其实是修改了嵌套对象,Object.isFrozen(obj)仍返回true - 给对象添加过 getter/setter 后再调用
Object.freeze,若未显式设置configurable: false,Object.isFrozen会返回false
在更新前用 Object.isFrozen 做守卫的典型写法
不是所有“不可变”都需要拒绝更新——比如你只想阻止对配置对象的结构或键名变更,但允许深层赋值。这时应明确守卫意图:
- 如果目标是「禁止任何属性增删改」,直接用
if (Object.isFrozen(target)) { return; } - 如果只是「避免意外覆盖关键字段」,建议配合
Object.getOwnPropertyDescriptors检查具体属性状态,而非全量冻结判断 - 注意:
Object.isFrozen对null或原始值(如42、"str")返回true,需提前if (typeof obj === 'object' && obj !== null)过滤
简单示例:
function safeUpdate(target, updates) {
if (typeof target !== 'object' || target === null || Object.isFrozen(target)) {
console.warn('target is frozen or invalid, skip update');
return;
}
Object.assign(target, updates);
}
Object.isFrozen 在 Proxy 和 deepFreeze 场景下的局限性
Object.isFrozen 是浅层检测,对 Proxy 实例始终返回 false(即使 handler 完全拦截了所有操作),也无法识别手动实现的“逻辑冻结”。
- 用
deepFreeze递归冻结嵌套对象后,Object.isFrozen仍只反映最外层状态;内层对象可能被单独解冻而无感知 - 某些库(如 immer)内部使用代理模拟不可变,此时
Object.isFrozen完全失效,应改用其提供的isDraft或类似标识 - V8 引擎中,
Object.isFrozen是快路径检查,性能好但语义窄;不要把它当作“运行时不可变断言”的替代品
为什么有时 Object.isFrozen 返回 false 却依然无法赋值
常见于属性级锁定:对象可扩展、可配置,但某个关键属性被设为 writable: false。例如:
const obj = {};
Object.defineProperty(obj, 'id', { value: 1, writable: false });
// 此时 Object.isFrozen(obj) → false
obj.id = 2; // 静默失败(非严格模式)或报错(严格模式)
这种情况下,靠 Object.isFrozen 守卫毫无意义。真正需要的是:
- 检查特定属性:用
Object.getOwnPropertyDescriptor(obj, 'id')?.writable - 批量更新前做
Object.keys(updates).every(key => Object.getOwnPropertyDescriptor(obj, key)?.writable !== false) - 或者统一用
Object.freeze初始化,避免混用 defineProperty 和赋值
冻结状态容易被局部操作绕过,真正可靠的更新守卫得结合业务语义,而不是只依赖一个布尔值。










