深拷贝应避免json.parse(json.stringify()),因其丢失date/regexp/map/set/undefined/function/symbol等;首选structuredclone(现代环境,支持循环引用但不支持function/symbol);兼容旧环境或需完整类型支持时用lodash clonedeep;手写需weakmap防循环+精准类型判断。

要实现 JavaScript 对象深拷贝且不丢失原始类型(如 Date、RegExp、Map、Set、BigInt、undefined、Symbol、函数等),关键在于避免使用 JSON.parse(JSON.stringify(obj)) —— 它会静默丢弃这些值,或将其转为错误形态(例如 Date 变字符串,undefined 消失,function 被忽略)。
优先用 structuredClone(现代浏览器首选)
这是目前最接近“开箱即用、保类型、防循环”的原生方案:
- 支持
Date、RegExp、Map、Set、ArrayBuffer、TypedArray、Error、BigInt等多种内置类型 - 自动处理循环引用,不会报错
- 保留可枚举属性和嵌套结构,不破坏数据语义
- 但注意:不支持
function、undefined、Symbol、DOM 节点和原型方法;若对象含这些,需提前过滤或降级处理
用法很简单:
const copied = structuredClone(original);需要兼容旧环境或支持函数/Symbol?选 Lodash cloneDeep
当项目需支持 IE 或要求完整覆盖边界类型(包括函数、Symbol、不可枚举属性、自定义类实例等),lodash.cloneDeep 仍是工业级最稳的选择:
- 能正确复制
function(作为引用保留)、Symbol键与值、undefined字段、Error实例、自定义构造器对象 - 内置循环引用检测,无需手动维护缓存
- 对
Map/Set/WeakMap/WeakSet等集合类型有专门逻辑 - 缺点是引入约 70KB(gzip 后约 24KB),如非必要,不建议为简单场景引入
示例:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
const copied = cloneDeep(original);
手写可控深拷贝:适合学习或轻量定制
若想完全掌控行为(比如跳过某些字段、转换特定类型、保留原型链),可手写递归函数,核心要点包括:
- 用
Object.prototype.toString.call(x)精准识别类型(比typeof和instanceof更可靠) - 对
Date、RegExp、Map、Set等分别新建实例并复制内容 - 用
WeakMap缓存已拷贝源对象,遇到重复引用直接返回,防止栈溢出 - 基础类型(
string/number/boolean/null/undefined/symbol/bigint)直接返回 - 函数通常不拷贝(因无法真正“复制”执行逻辑),可选择跳过、浅引用或序列化后还原(不推荐)
示例骨架(不含 Symbol/undefined 函数支持,仅作示意):
function deepClone(obj, seen = new WeakMap()) {if (obj === null || typeof obj !== 'object') return obj;
if (seen.has(obj)) return seen.get(obj);
if (obj instanceof Date) return new Date(obj);
if (obj instanceof RegExp) return new RegExp(obj);
if (obj instanceof Map) return new Map([...obj]);
if (obj instanceof Set) return new Set([...obj]);
const cloned = Array.isArray(obj) ? [] : {};
seen.set(obj, cloned);
for (const [k, v] of Object.entries(obj)) {
cloned[k] = deepClone(v, seen);
}
return cloned;
}
什么情况下仍可用 JSON 方法?
它不是“错”,而是适用场景极窄:仅限纯数据对象(Plain Old Data),即只含 string、number、boolean、null、Array、Object,且无循环、无 undefined、无 Date 等特殊值。例如配置项、API 响应体预处理时可快速使用:
但只要不确定数据结构是否“纯净”,就别依赖它保类型。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










