JSON.parse(JSON.stringify(obj))可实现纯数据对象的简易深拷贝,但不支持函数、undefined、Symbol、Date、RegExp、Map、Set、BigInt及循环引用,且会丢失原型链和特殊对象类型。

JavaScript 中用 JSON.stringify() 和 JSON.parse() 组合可以实现简单对象的深拷贝和序列化,但它有明显限制,不能处理函数、undefined、Symbol、Date、RegExp、Map、Set、BigInt 等类型,也不支持循环引用。
JSON 方法如何实现“伪深拷贝”
所谓“JSON 深拷贝”,本质是先将对象转为 JSON 字符串(stringify),再解析回新对象(parse)。这过程会丢弃无法序列化的属性,生成一个**结构相同但完全独立的新对象**,对纯数据对象(即只含 null、布尔、数字、字符串、数组、普通对象)有效。
- 原始对象必须是可序列化的:不能含函数、undefined、Symbol、Date 实例等
- 所有键名会被强制转为字符串,数值键也会被保留但类型变为字符串(如
{1: 'a'}→{"1": "a"}) - 原型链和 constructor 信息全部丢失,结果只是 plain object
- 示例:✔️ 有效:
const copy = JSON.parse(JSON.stringify({a: 1, b: [2, 3], c: {d: 4}})); - 示例:❌ 失效:
JSON.stringify({fn() {}, date: new Date(), undef: undefined})→{"date":"...","undef":null}(函数消失,undefined 变 null,Date 变字符串)
JSON 序列化与反序列化的典型用途
它最常用于浏览器端跨上下文传递纯数据,比如 localStorage 存储、fetch 请求体、postMessage 通信、URL 参数拼接等场景。这些地方天然要求数据可安全转为字符串,且不依赖运行时类型。
- 存入 localStorage:
localStorage.setItem('user', JSON.stringify({name: 'Alice', age: 30})) - 从 localStorage 读取并还原:
const user = JSON.parse(localStorage.getItem('user')) || {} - 作为 fetch 请求体:
fetch('/api', {method: 'POST', body: JSON.stringify(data)}) - 注意:服务端返回 JSON 时,前端通常直接
response.json()(本质是封装好的JSON.parse)
哪些情况绝对不能用 JSON 做深拷贝
一旦对象中存在以下任一成分,JSON 方案就会出错或静默丢失信息:
立即学习“Java免费学习笔记(深入)”;
-
函数或 class 方法:直接被忽略(
JSON.stringify({f() {}})→{}) -
Date / RegExp / Error 对象:Date 变字符串,RegExp 变空对象
{},Error 通常为空 -
undefined / Symbol / BigInt:undefined 被跳过或变 null;Symbol 和 BigInt 直接报错
TypeError - Map / Set / ArrayBuffer / TypedArray / Blob:无法序列化,多数报错或转为空对象/数组
-
循环引用:
const obj = {}; obj.self = obj;→JSON.stringify(obj)报错Converting circular structure to JSON
更健壮的替代方案建议
生产环境需可靠深拷贝时,推荐按需选择:
- 现代浏览器 + 简单结构:用
structuredClone()(Chrome 98+、Firefox 94+、Safari 15.4+ 支持),能处理 Map、Set、Date、RegExp、ArrayBuffer 等,且支持循环引用 - 需要兼容旧环境:使用 Lodash 的
_.cloneDeep(),覆盖全面,经久验证 - 自定义控制:用
JSON.stringify的replacer参数预处理(如把 Date 转 ISO 字符串),再配合reviver在parse时还原——但这仍是手工补丁,非通用解法 - 仅需浅层拷贝数组或对象:优先用展开语法
[...arr]或{...obj},性能好、语义清


















