首页 > web前端 > js教程 > 正文

js 如何使用cloneDeep深度克隆对象数组

煙雲
发布: 2025-08-12 08:01:01
原创
290人浏览过

要深度克隆对象数组,必须使用能递归复制嵌套对象的方法;2. json.parse(json.stringify()) 虽常用,但会丢失函数、undefined、symbol、bigint,将日期转为字符串,正则变空对象,且不支持循环引用;3. lodash 的 _.clonedeep() 能处理日期、正则、map、set 等复杂类型,是更稳健的方案;4. 原生 structuredclone() api 支持日期、正则、数组、嵌套对象甚至循环引用,但不兼容老版本浏览器,是未来推荐的轻量选择;5. 自行实现递归克隆函数可行但复杂,需处理类型判断和循环引用,一般不建议。深度克隆的本质是切断引用关系,确保新旧对象完全独立,避免修改副本影响原始数据,这在状态管理等场景中至关重要。

js 如何使用cloneDeep深度克隆对象数组

在 JavaScript 里要深度克隆一个对象数组,可不是简单地用

...
登录后复制
展开运算符或者
slice()
登录后复制
就能搞定的。那些方法只能做“浅克隆”,意思是数组里的基本类型值被复制了,但如果数组元素是对象,复制的只是它们的引用。要真正做到“深度”,让每个嵌套的对象也独立出来,你需要更强力的手段,比如利用 JSON 的序列化和反序列化,或者引入像 Lodash 这样的工具库,再或者,自己写一个递归函数来精细控制。

说实话,在 JavaScript 里处理深度克隆,尤其是一个包含复杂对象的数组,最常见也最“偷懒”的方法就是

JSON.parse(JSON.stringify(originalArray))
登录后复制
。这招简单粗暴,但有效,它把你的数组先变成一个 JSON 字符串,再解析回来,过程中就切断了所有原始引用。不过,这招有明显的局限性,比如它会丢失函数、
undefined
登录后复制
Symbol
登录后复制
BigInt
登录后复制
,日期对象会变成字符串,正则表达式会变成空对象等等。所以,如果你的数据结构比较纯粹,都是可序列化的基本类型和普通对象,那它够用。

如果你追求更稳健、更全面的解决方案,Lodash 库提供的

_.cloneDeep()
登录后复制
方法几乎是行业标准。它能处理各种复杂的数据类型,包括日期、正则表达式、Map、Set,甚至循环引用(虽然处理循环引用时它有自己的策略)。在大型项目里,引入 Lodash 的这个方法,能省去你不少麻烦,而且代码可读性也很好。

// 使用 Lodash 的 _.cloneDeep
import _ from 'lodash';

const originalArray = [
  { id: 1, name: 'Item A', details: { value: 10, tags: ['new', 'hot'] }, createdAt: new Date() },
  { id: 2, name: 'Item B', details: { value: 20, tags: ['old'] }, func: () => console.log('hi') }
];

const clonedArray = _.cloneDeep(originalArray);

// 验证深度克隆
clonedArray[0].details.value = 100;
clonedArray[1].name = 'Item C';
clonedArray[0].createdAt.setFullYear(2000); // 修改克隆后的日期

console.log('Original Item A Value:', originalArray[0].details.value); // 应该还是 10
console.log('Cloned Item A Value:', clonedArray[0].details.value);     // 应该是 100
console.log('Original Item B Name:', originalArray[1].name);         // 应该还是 'Item B'
console.log('Cloned Item B Name:', clonedArray[1].name);             // 应该是 'Item C'
console.log('Original Item A Date Year:', originalArray[0].createdAt.getFullYear()); // 应该还是当前年份
console.log('Cloned Item A Date Year:', clonedArray[0].createdAt.getFullYear());     // 应该是 2000
console.log('Original Item B Func:', originalArray[1].func); // Lodash会丢失函数,这里会是 undefined
登录后复制

对于那些对性能有极致要求,或者数据结构极其特殊,且不想引入第三方库的场景,你也可以考虑自己实现一个递归的深度克隆函数。但这通常比较复杂,需要处理各种边缘情况,比如循环引用,不同类型的对象(数组、日期、正则、Map、Set等),以及原型链的继承问题。一般情况下,不建议自己造这个轮子。

为什么简单的赋值或展开运算符不能实现深度克隆?

这其实是 JavaScript 中一个非常基础但也容易让人迷惑的概念:值类型和引用类型。当你用

const newArr = oldArr;
登录后复制
或者
const newArr = [...oldArr];
登录后复制
这样的方式去“复制”一个数组时,如果数组里面存放的是基本数据类型(比如数字、字符串、布尔值),那它们确实会被复制一份。但一旦数组里有对象(包括普通对象、数组、函数等),你复制的就不是对象本身,而是指向这些对象的“引用”或者说“地址”。这就像你复制了一本书的目录,而不是书的全部内容。

所以,当你修改

newArr[0].name
登录后复制
时,因为
newArr[0]
登录后复制
oldArr[0]
登录后复制
指向的是内存中的同一个对象,你实际上修改的是同一个对象,结果就是
oldArr[0].name
登录后复制
也会跟着变。这在实际开发中一不小心就可能导致意想不到的副作用,尤其是在处理组件状态、数据不可变性这些场景时,简直是“灾难现场”。你本以为自己操作的是一份独立的数据,结果却影响了原始数据,这可不是闹着玩的。理解这一点,是掌握 JavaScript 数据操作的关键一步。

使用 JSON.parse(JSON.stringify()) 进行深度克隆有哪些坑?

JSON.parse(JSON.stringify())
登录后复制
这种方式虽然简单,但它的“坑”可不少,尤其是在你处理复杂数据时。它的核心原理是利用 JSON 规范来序列化和反序列化数据,而 JSON 规范对数据类型有着严格的限制。

首先,它会丢失函数。对象里的方法或者任何函数属性都会在序列化过程中被直接忽略。因为 JSON 压根就不支持存储函数。

Stable Video
Stable Video

Stability AI 发布的开源AI视频大模型,用文字或图像创建视频,把你的概念变成迷人的电影

Stable Video 227
查看详情 Stable Video
const objWithFunc = {
  name: 'Test',
  greet: function() { console.log('Hello'); }
};
const cloned = JSON.parse(JSON.stringify(objWithFunc));
console.log(cloned.greet); // undefined
登录后复制

其次,

undefined
登录后复制
Symbol
登录后复制
BigInt
登录后复制
也会被忽略
。它们在 JSON 字符串中无法表示,所以会被直接“抹掉”。

const objWithSpecial = {
  a: undefined,
  b: Symbol('sym'),
  c: 10n // BigInt
};
const cloned = JSON.parse(JSON.stringify(objWithSpecial));
console.log(cloned); // {} - 什么都没了
登录后复制

再来,

Date
登录后复制
对象会变成字符串。日期对象会被转换成 ISO 8601 格式的字符串,而不是一个
Date
登录后复制
对象实例。如果你需要对日期进行操作,还得手动转换回来。

const objWithDate = {
  eventDate: new Date()
};
const cloned = JSON.parse(JSON.stringify(objWithDate));
console.log(cloned.eventDate);       // "2023-10-27T08:30:00.000Z" (一个字符串)
console.log(cloned.eventDate instanceof Date); // false
登录后复制

还有,

RegExp
登录后复制
对象会变成空对象。正则表达式对象也会在序列化后变成一个普通的空对象
{}
登录后复制

const objWithRegExp = {
  pattern: /abc/g
};
const cloned = JSON.parse(JSON.stringify(objWithRegExp));
console.log(cloned.pattern); // {}
登录后复制

最后,它不能处理循环引用。如果你的对象结构中存在循环引用(比如 A 引用 B,B 又引用 A),

JSON.stringify()
登录后复制
会直接抛出一个
TypeError: Converting circular structure to JSON
登录后复制
错误。这是它最大的局限性之一。

所以,虽然

JSON.parse(JSON.stringify())
登录后复制
用起来方便,但你得清楚它的适用场景和这些潜在的“陷阱”。一旦数据里包含上述任何一种类型,你就得考虑其他更健壮的方案了。

除了 Lodash,还有哪些库或方法可以实现更健壮的深度克隆?

Lodash 的

_.cloneDeep
登录后复制
确实是多数开发者的首选,但 JavaScript 生态系统一直在进化,我们也有了更多原生的或者其他库的选择。

一个非常值得关注的,是现代浏览器和 Node.js(v17+)提供的原生 API:

structuredClone()
登录后复制
。这个方法原本是用于 Web Workers 之间传递消息的,现在被暴露出来,可以直接用于深度克隆。它的强大之处在于,它能够处理许多
JSON.parse(JSON.stringify())
登录后复制
无法处理的数据类型,包括
Date
登录后复制
、`

以上就是js 如何使用cloneDeep深度克隆对象数组的详细内容,更多请关注php中文网其它相关文章!

最佳 Windows 性能的顶级免费优化软件
最佳 Windows 性能的顶级免费优化软件

每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。

下载
来源:php中文网
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
最新问题
开源免费商场系统广告
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板
关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号