深拷贝不可扩展对象时,新对象默认可扩展,需手动调用object.freeze()等方法还原状态;structuredclone不保留冻结/密封元信息,仅复制数据内容。

深拷贝不可扩展对象(Object.preventExtensions()、Object.seal() 或 Object.freeze() 的对象)时,关键不是“绕过”限制,而是尊重其不可扩展性——新拷贝对象默认是可扩展的,但若需保持原对象的扩展状态(如冻结),必须在拷贝完成后手动还原。
不可扩展对象的深拷贝核心原则
JavaScript 深拷贝本身不继承源对象的元属性(如 [[Extensible]]、[[Writable]]、[[Configurable]])。无论用 structuredClone 还是手写递归,生成的新对象总是可扩展、可配置、属性可写的。要真正“保留不可扩展性”,需分两步:先完成深拷贝,再显式设置对应元状态。
structuredClone 能否保留冻结/密封状态?
不能。structuredClone() 仅复制数据内容与类型(如 Date、Map、循环引用等),完全忽略对象的属性描述符和扩展性元信息。例如:
复制AI写代码
1 const frozen = Object.freeze({ a: 1, b: { c: 2 } });
2 const copy = structuredClone(frozen);
3 console.log(Object.isFrozen(copy)); // false
4 Object.defineProperty(copy, 'd', { value: 3 }); // ✅ 成功 —— copy 是可扩展的
如何手动还原不可扩展状态?
拷贝完成后,用 Object.preventExtensions()、Object.seal() 或 Object.freeze() 对拷贝结果再次施加相同约束。注意:这些方法只作用于对象自身,不影响嵌套子对象,所以需递归处理(如果需要全树冻结):
-
仅顶层冻结:直接对拷贝结果调用
Object.freeze(copy) -
全树冻结(含所有嵌套对象):需递归遍历,对每个普通对象(非 Date/RegExp/Map 等内置类型)执行
Object.freeze() -
保留属性描述符(如 writable/configurable):必须用
Object.getOwnPropertyDescriptors()+Object.defineProperties()配合深拷贝,且仅适用于浅层属性;深层仍需递归处理
推荐做法:按需选择,不盲目冻结
大多数业务场景中,深拷贝后对象本就该是可修改的(否则拷贝失去意义)。只有明确需要“不可变快照”时,才额外冻结。此时建议:
- 优先使用
structuredClone()完成数据拷贝(它天然支持 Date/Map/循环引用等) - 再用
Object.freeze()封装顶层,或封装一个deepFreeze()工具函数 - 避免在深拷贝函数内部自动冻结——这会污染纯数据复制职责,也违背不可变设计中“冻结应显式、有语义”的原则
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











