- Published on
JS中JSON.parse(JSON.stringify())深拷贝的缺点
- Authors

- 作者
- kai
目录

JS中JSON.parse(JSON.stringify())深拷贝的缺点
推荐使用 lodash.cloneDeep 实现可靠深拷贝(避坑 JSON.parse/stringify)
// 推荐按需引入(仅加载 cloneDeep 模块)
import cloneDeep from 'lodash/cloneDeep';
// 用法示例
const original = { name: '测试', list: [1, 2, 3] };
const cloned = cloneDeep(original);
很多开发者会用 JSON.parse(JSON.stringify()) 临时实现深拷贝,但它存在诸多局限性,仅适用于纯 JSON 结构数据。以下通过具体案例说明其问题,同时补充更全面的避坑指南。
一、案例数据(覆盖常见数据类型)
const testObj = {
basic: '纯字符串数据',
date: new Date('2025-01-01'), // 日期类型
reg: new RegExp(/\w+/, 'g'), // 正则类型
func: () => console.log('测试函数'), // 函数
error: new Error('自定义错误'), // 错误对象
symbol: Symbol('唯一标识'), // Symbol
undef: undefined, // undefined
nullVal: null, // null
nan: NaN, // 非数字
infinity: Infinity, // 无穷大
bigInt: 9007199254740991n, // BigInt 类型
nested: { a: 1, b: [2, 3] } // 嵌套对象
};
二、JSON.parse/stringify 的核心问题
1. 数据丢失/类型异常
执行拷贝后,多种类型会出现丢失或转义问题:
const faultyClone = JSON.parse(JSON.stringify(testObj));
console.log(faultyClone);
实际结果:
- 函数
func、Symbolsymbol、undefined直接丢失 - 日期
date转为字符串("2024-12-31T16:00:00.000Z"),丢失日期类型 - 正则
reg、错误对象error变为空对象{} NaN、Infinity转为nullBigInt直接报错(TypeError: Do not know how to serialize a BigInt)
2. 循环引用直接报错
若对象存在循环引用(A 引用 B,B 引用 A),拷贝会直接抛出异常:
const circleObj = {};
circleObj.self = circleObj; // 循环引用:自身引用自身
// 报错:Converting circular structure to JSON
JSON.parse(JSON.stringify(circleObj));
而 lodash.cloneDeep 可正常处理循环引用,不会报错且拷贝完整。
3. 构造函数丢失
自定义构造函数创建的实例,拷贝后会丢失原型链和 constructor,变为普通对象:
// 自定义构造函数
function User(name) {
this.name = name;
this.sayHi = () => console.log(`Hi, ${this.name}`);
}
const user = new User('张三');
const clonedUser = JSON.parse(JSON.stringify(user));
console.log(user instanceof User); // true(原始实例)
console.log(clonedUser instanceof User); // false(变为普通对象)
console.log(clonedUser.sayHi); // undefined(方法丢失)
4. 特殊值处理异常
除了常见类型,以下特殊场景也会出问题:
- 数组中的
undefined会转为null([1, undefined, 3]→[1, null, 3]) - 正则的 flags(如全局匹配 g)会丢失
- 错误对象的 stack 调用栈信息丢失
三、两种方案对比表
| 特性/场景 | JSON.parse(JSON.stringify()) | lodash.cloneDeep |
|---|---|---|
| 纯 JSON 数据(字符串、数字、嵌套对象) | 支持(正常拷贝) | 支持(正常拷贝) |
| 日期、正则、函数、Symbol | 不支持(丢失/转义) | 支持(完整拷贝) |
| 循环引用对象 | 不支持(直接报错) | 支持(正常处理) |
| 自定义构造函数实例 | 不支持(丢失原型) | 支持(保留原型) |
| BigInt、NaN、Infinity | 不支持(报错/转义) | 支持(正常保留) |
| 嵌套层级极深的对象 | 可能栈溢出 | 优化较好,不易溢出 |
四、使用建议
- 仅纯 JSON 数据:可临时使用
JSON.parse(JSON.stringify()),但需确保无特殊类型 - 生产环境/复杂数据:优先使用
lodash.cloneDeep,兼顾可靠性和兼容性 - 轻量场景(无循环引用):也可使用
structuredCloneAPI(浏览器原生支持,无需依赖),但不支持函数、Symbol 等类型
// 原生 structuredClone 示例(无依赖,有限支持)
const nativeClone = structuredClone(testObj);
// 注意:不支持函数、Symbol、循环引用,仅适用于部分场景
总结
JSON.parse(JSON.stringify()) 是"临时方案",存在诸多隐式坑;为保证代码健壮性,尤其是处理复杂数据时,推荐使用 lodash.cloneDeep 这类成熟工具方法。
