JavaScript深拷贝无万能方案:structuredClone()支持多数内置类型且处理循环引用,但不支持function等;手写需防null、数组遍历、循环引用等陷阱;Lodash cloneDeep覆盖广但有体积和兼容性考量;JSON方法仅适用于纯数据对象。

JavaScript 深拷贝没有“银弹”,JSON.parse(JSON.stringify(obj)) 看似简单,但会丢函数、undefined、Symbol、Date、RegExp、Map、Set、循环引用等;真正可靠的方式得按场景选。
用 structuredClone() —— 现代浏览器首选
这是目前最接近“开箱即用”的原生深拷贝方案,支持大多数内置类型(包括 Date、RegExp、Map、Set、ArrayBuffer、TypedArray),且能处理循环引用。
注意点:
- Node.js 17+ 默认启用,18.10+ 稳定;低于此版本需加
--experimental-structured-cloning标志 - 不支持
function、undefined、Symbol、Promise、Error实例(会抛错或跳过) - 不能拷贝带有
prototype方法的自定义类实例(只保留数据属性)
const obj = { a: 1, b: new Date(), c: new Map([[1, 'x']]) };
const cloned = structuredClone(obj); // ✅ 安全、快、语义清晰
手写递归 + 类型判断 —— 需要完全控制逻辑时
当你要保留 function、undefined、Symbol,或需对特定字段做定制处理(如忽略某些 key、转换日期格式),就得自己实现。关键不是“写全所有类型”,而是明确你的数据契约。
立即学习“Java免费学习笔记(深入)”;
常见陷阱:
- 忘记处理
null(它 typeof 是"object",但不是对象) - 用
for...in遍历数组,导致索引变成字符串、丢失稀疏性 - 没用
Object.prototype.toString.call()或Array.isArray()判数组,误把类数组当普通对象 - 没缓存已遍历对象,遇到循环引用直接栈溢出
function deepClone(obj, seen = new WeakMap()) {
if (obj === null || typeof obj !== 'object') return obj;
if (seen.has(obj)) return seen.get(obj);
const cloned = Array.isArray(obj) ? [] : {};
seen.set(obj, cloned);
for (const key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key)) {
cloned[key] = deepClone(obj[key], seen);
}
}
return cloned;
}
用第三方库 —— Lodash 的 cloneDeep() 最常用
它覆盖了绝大多数边界情况:Buffer、Promise、HTML 元素、arguments、带 constructor 的对象等。但要注意体积和 tree-shaking 支持。
使用前确认:
- 是否真需要它支持的所有类型?比如纯 JSON 数据流,
structuredClone更轻量 - 项目是否已引入 Lodash?避免只为一个函数引入整包(可用
lodash.clonedeep单独安装) - 某些环境(如 Deno 或严格 CSP)可能禁用
eval或Function构造器,而 Lodash 内部有动态代码生成逻辑(极少数情况)
import cloneDeep from 'lodash.clonedeep';
const data = { fn: () => {}, date: new Date() };
const copied = cloneDeep(data); // ✅ fn 被保留为 function,date 正确复制
为什么不用 JSON.parse(JSON.stringify())?
它只适合“纯数据对象”——即键值全是字符串、数字、布尔、null、普通数组或嵌套对象。一旦出现以下任一,结果就不可靠:
-
undefined和function字段直接被忽略 -
Symbol键被丢弃 -
Date变成字符串("2024-05-20T12:00:00.000Z"),不再是Date实例 -
NaN、Infinity变成null - 循环引用直接报错:
TypeError: Converting circular structure to JSON
const bad = { a: undefined, b: () => {}, c: Symbol('x'), d: new Date() };
console.log(JSON.parse(JSON.stringify(bad))); // { d: "2024-05-20T12:00:00.000Z" } —— 其他全没了
深拷贝真正的复杂点不在“怎么写”,而在“你到底想拷什么”。函数要不要?原型链要不要?特殊对象(如 canvas 上下文)要不要?先画清数据边界,再选工具,比背方法更重要。











