深拷贝需还原object.freeze、object.seal和object.preventextensions的约束语义:冻结对象克隆后须object.freeze();密封对象需object.preventextensions()并重设各属性configurable为false;不可扩展对象仅需object.preventextensions()。

深拷贝过程中遇到 Object.freeze 或 Object.seal 的对象时,不能简单地“复制状态”,而要主动还原其约束语义——即新对象不仅要内容一致,还要在可变性权限上与原对象对齐。否则,克隆后本该只读的字段可能被意外修改,破坏业务逻辑的安全边界。
冻结对象(frozen)需在克隆后立即冻结
冻结对象的所有属性都不可写、不可配置、不可枚举,且对象本身不可扩展。深拷贝生成的新对象默认是可扩展、可配置的,必须显式调用 Object.freeze() 才能复现原始约束。
- 先完成递归深拷贝(含 Symbol、访问器、原型链等),得到一个结构一致但“裸”的新对象
- 再对其执行
Object.freeze(clone),确保所有属性和对象自身状态完全锁定 - 注意:
Object.freeze()是浅层冻结,若内部仍有嵌套冻结对象,需在递归过程中逐层判断并同步冻结
密封对象(sealed)需保留不可扩展性 + 不可配置性
密封对象允许修改已有属性值,但禁止新增、删除或改变属性描述符(如 writable、configurable)。克隆时需分两步还原:
- 用
Object.preventExtensions()禁止新增属性 - 遍历所有自有属性,用
Object.defineProperty()重设其configurable: false,同时保留writable和enumerable原始值 - 若原对象含访问器(getter/setter),需通过
Object.getOwnPropertyDescriptor()获取完整描述符并原样复写
不可扩展对象(non-extensible)只需阻止新增属性
这类对象仅限制添加新属性,其余操作(改值、删属性、改描述符)仍允许。克隆后只需调用 Object.preventExtensions() 即可匹配原始状态。
- 无需改动已有属性的
configurable或writable - 也不影响原型链或 Symbol 属性的处理逻辑
- 注意:该状态不具传递性,子对象是否可扩展需单独判断
统一检测与状态继承机制
为避免手动判断,可在深拷贝函数中加入状态识别逻辑:
- 使用
Object.isFrozen(obj)、Object.isSealed(obj)、Object.isExtensible(obj)三者按顺序判断优先级(frozen ⊃ sealed ⊃ non-extensible) - 对每个被拷贝的引用类型节点,在递归返回前根据原始状态施加对应保护
- 若原对象是
frozen,则克隆后必须freeze;若是sealed,则先preventExtensions再重定义属性;若是non-extensible,仅preventExtensions











