深拷贝不保留原对象的冻结、密封或不可扩展状态,只复制数据值;需手动调用Object.freeze()等方法恢复元属性,且深冻结须递归实现。

深拷贝本身的目标是创建一个完全独立的新对象,而不可扩展(Object.preventExtensions)或冻结(Object.freeze)的对象,其“不可变性”属于元属性(meta-level),不是数据内容的一部分。因此,**深拷贝操作默认不保留原对象的可扩展性、密封性或冻结状态**——它只复制值,不复制这些控制行为的内部标志。
深拷贝会自动忽略冻结/不可扩展状态
无论你用哪种深拷贝方法(structuredClone、JSON.parse(JSON.stringify())、lodash.cloneDeep 或手写递归),生成的新对象都是普通、可扩展、可修改的:
-
structuredClone(obj)返回的是全新对象,不会冻结,即使obj是Object.freeze()过的 -
JSON.parse(JSON.stringify(frozenObj))输出的也是普通对象,所有冻结/密封信息彻底丢失 - 第三方库如
lodash.cloneDeep同样只关心数据结构,不继承元属性
如果你需要“连冻结状态一起复制”,就得手动处理
JavaScript 没有内置 API 能直接克隆元属性(比如把冻结状态也深拷贝过去)。若业务逻辑明确要求新对象保持与原对象一致的不可变性级别(例如:原对象被 freeze,你也希望副本一并 freeze),需在深拷贝后**显式调用对应方法**:
- 先深拷贝:
const copy = structuredClone(original); - 再根据原对象状态补操作:
- 若
Object.isFrozen(original)为true→Object.freeze(copy); - 若
Object.isSealed(original)为true且非冻结 →Object.seal(copy); - 若
Object.isExtensible(original)为false且未密封 →Object.preventExtensions(copy);
- 若
注意深冻结(deep freeze)不能靠深拷贝实现
常见误区:以为深拷贝能解决“嵌套冻结”。其实 Object.freeze 是浅层的——它只冻结自身属性,不递归冻结子对象。所以即使你深拷贝了一个已深冻结的对象,副本仍是普通对象;反过来,对深拷贝结果执行 Object.freeze,也只冻结第一层。
若你需要副本也具备相同层级的冻结能力,必须手写递归冻结函数(类似你知识库中提到的 deepFreeze),并在深拷贝完成后调用它:
function deepFreeze(obj) {
Object.freeze(obj);
for (const key in obj) {
if (obj.hasOwnProperty(key) && obj[key] !== null && typeof obj[key] === 'object') {
deepFreeze(obj[key]);
}
}
return obj;
}
const copy = structuredClone(original);
deepFreeze(copy); // 此时 copy 才真正“深冻结”
实际开发中的建议
绝大多数场景下,你不需要复制冻结状态——深拷贝的目的本就是获得可安全修改的副本。强行还原冻结反而可能阻碍后续逻辑(比如想临时改配置再还原)。只有在极少数特殊需求中(如构建不可变状态快照系统、沙箱环境隔离),才需额外封装“带元属性复制”的工具函数。此时推荐封装成统一方法,避免每次手动判断:
- 检查原对象的
isFrozen/isSealed/isExtensible - 深拷贝后,按优先级链式应用
freeze → seal → preventExtensions(因freeze已包含后两者) - 对数组、Map、Set 等内置类型,注意它们没有
freeze方法,只能冻结包装对象
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











