json.parse(json.stringify(obj)) 无法深拷贝 function、regexp 和 undefined,因 json 标准不支持;推荐优先用 structuredclone()(现代环境),其次手写递归(兼容旧版、可控性强),或 lodash + customizer 定制处理。

直接用 JSON.parse(JSON.stringify(obj)) 做深拷贝时,Function、RegExp 和 undefined 会彻底丢失或失真——这不是 bug,而是 JSON 标准本身不支持这三类值。想规避,关键不是“绕过限制”,而是换掉整个流程,用可编程控制的方案替代纯序列化路径。
用 structuredClone() 优先尝试
现代环境(Chrome 98+、Firefox 94+、Node.js 18.13+/20.6+)已原生支持 structuredClone(),它能正确处理 RegExp 和 undefined(保留原始值),对 Function 则克隆引用(注意:函数体不变,但 this 和闭包行为需自行验证)。它天然防循环引用,无需额外处理。
- RegExp 实例会被完整复制,包括
source和flags - undefined 字段不会被跳过,仍保留在目标对象中
- 不支持 Symbol、Promise、DOM 节点等,但对上述三类缺陷已覆盖大部分场景
手写递归 clone,精准控制每种类型
当需要兼容旧环境,或必须保留函数体逻辑(如绑定新上下文)、或需处理 Symbol/不可枚举属性时,手写递归是最灵活的方式。核心是分类判断 + WeakMap 防循环:
- 遇到 Function:直接赋值,或用
value.bind({})创建无依赖副本 - 遇到 RegExp:新建实例
new RegExp(value.source, value.flags) - 遇到 undefined:不跳过,原样赋值(
cloned[key] = obj[key]) - 用
Reflect.ownKeys(obj)遍历所有键(含不可枚举、Symbol 键),确保不漏 - 用 WeakMap 缓存源对象与克隆对象映射,重复引用直接返回缓存结果
用 Lodash 并启用 customizer 保留函数
_.cloneDeep() 默认丢弃函数,但可通过 customizer 回调干预:
- 对函数类型显式返回自身:
return typeof value === 'function' ? value : undefined - 配合预处理,把 undefined 显式转为 null(再用
_.cloneDeep处理),避免字段消失 - RegExp 仍需在 customizer 中手动重建,例如检测
value instanceof RegExp后返回新实例
对 undefined 的处理要先问清语义
很多场景下,undefined 并非业务必需,而是初始化遗漏。更稳健的做法是:
- 接口约定空值统一用
null,而非undefined(JSON 安全且语义明确) - 若必须兼容含 undefined 的旧数据,可在深拷贝前做一次标准化:
JSON.stringify(obj, (k, v) => v === undefined ? null : v),再 parse + 深拷贝 - 不建议为保留 undefined 而强行用复杂递归——先确认它在你的数据流里是否真的代表一种有效状态











