数组深拷贝的核心是创建一个与原数组完全独立的新数组,修改新数组不会影响原数组。1. json序列化/反序列化:适用于仅含基本数据类型和普通对象且无循环引用的数组,优点是简单高效,缺点是无法处理函数、undefined、symbol及循环引用。2. 递归拷贝:可处理嵌套结构,需通过map记录已拷贝对象以避免循环引用导致的栈溢出,但仍无法直接复制函数和symbol。3. structuredclone:现代浏览器原生支持,性能较好且能处理date、regexp等特殊对象,但不兼容旧浏览器且无法复制函数和symbol。4. 浅拷贝后手动深拷贝:适用于仅需深拷贝部分元素的场景,可提升性能但需手动管理拷贝逻辑。5. lodash的_.clonedeep:功能最强大,支持复杂结构和循环引用,但需引入外部依赖且性能略低。选择方法应根据数组结构复杂度、是否含特殊类型、兼容性要求及性能需求综合判断。

数组深拷贝,说白了,就是创建一个新数组,这个新数组和原始数组完全独立,修改新数组不会影响到原始数组。

解决方案:
实现 JavaScript 数组深拷贝的方法有很多,各有优劣,选择哪个取决于你的具体需求和数组的复杂程度。
立即学习“Java免费学习笔记(深入)”;

这是最简单的方法之一,利用
JSON.stringify()
JSON.parse()
const originalArray = [1, 2, { a: 3 }];
const deepCopyArray = JSON.parse(JSON.stringify(originalArray));
deepCopyArray[2].a = 4; // 修改深拷贝后的数组
console.log(originalArray[2].a); // 输出 3,说明原始数组未被修改优点:

缺点:
undefined
Symbol
所以,如果你的数组只包含基本数据类型和普通对象,并且没有循环引用,那么这种方法是最快的。
递归拷贝是一种更通用的深拷贝方法,它可以处理更复杂的数据结构。
function deepCopy(obj) {
if (typeof obj !== 'object' || obj === null) {
return obj; // 如果不是对象或为 null,直接返回
}
const newObj = Array.isArray(obj) ? [] : {}; // 创建新对象或数组
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
newObj[key] = deepCopy(obj[key]); // 递归拷贝
}
}
return newObj;
}
const originalArray = [1, 2, { a: 3, b: function() { console.log('hello'); } }];
const deepCopyArray = deepCopy(originalArray);
deepCopyArray[2].a = 4;
console.log(originalArray[2].a); // 输出 3
deepCopyArray[2].b(); // 报错,deepCopy无法复制函数优点:
缺点:
undefined
Symbol
需要注意的是,递归拷贝需要处理循环引用的问题,否则会陷入无限循环。一种常见的处理方式是使用
Map
Set
structuredClone
structuredClone
const originalArray = [1, 2, { a: 3, b: function() { console.log('hello'); }, c: undefined, d: Symbol('test') }];
const deepCopyArray = structuredClone(originalArray);
deepCopyArray[2].a = 4;
console.log(originalArray[2].a); // 输出 3
// deepCopyArray[2].b(); // 报错,structuredClone无法复制函数
console.log(deepCopyArray[2].c); // undefined
// console.log(deepCopyArray[2].d); // 报错,structuredClone无法复制Symbol优点:
Date
RegExp
缺点:
Symbol
structuredClone
有时候,你只需要深拷贝数组中的部分元素,而不是整个数组。这时,可以先进行浅拷贝,然后手动深拷贝需要深拷贝的元素。
const originalArray = [1, 2, { a: 3 }, 4];
const shallowCopyArray = [...originalArray]; // 浅拷贝
shallowCopyArray[2] = deepCopy(originalArray[2]); // 手动深拷贝对象
shallowCopyArray[2].a = 4;
console.log(originalArray[2].a); // 输出 3优点:
缺点:
这种方法适用于只需要深拷贝数组中少量元素的场景,例如,数组中大部分元素都是基本数据类型,只有少数元素是对象或数组。
_.cloneDeep
lodash 是一个流行的 JavaScript 工具库,提供了很多实用的函数,包括深拷贝函数
_.cloneDeep
const _ = require('lodash');
const originalArray = [1, 2, { a: 3, b: function() { console.log('hello'); } }];
const deepCopyArray = _.cloneDeep(originalArray);
deepCopyArray[2].a = 4;
console.log(originalArray[2].a); // 输出 3
// deepCopyArray[2].b(); // 报错,lodash默认无法复制函数优点:
缺点:
lodash 的
_.cloneDeep
哪种方法最适合你?
JSON.stringify()
JSON.parse()
structuredClone
structuredClone
_.cloneDeep
循环引用是指对象之间相互引用,例如:
const obj1 = { a: 1 };
const obj2 = { b: obj1 };
obj1.c = obj2; // obj1 引用 obj2,obj2 引用 obj1,形成循环引用如果使用递归拷贝,遇到循环引用会导致无限递归,最终栈溢出。为了解决这个问题,可以使用
Map
Set
function deepCopy(obj, map = new Map()) {
if (typeof obj !== 'object' || obj === null) {
return obj;
}
if (map.has(obj)) {
return map.get(obj); // 如果已经拷贝过,直接返回
}
const newObj = Array.isArray(obj) ? [] : {};
map.set(obj, newObj); // 记录已经拷贝过的对象
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
newObj[key] = deepCopy(obj[key], map);
}
}
return newObj;
}
const obj1 = { a: 1 };
const obj2 = { b: obj1 };
obj1.c = obj2;
const deepCopyObj1 = deepCopy(obj1);
deepCopyObj1.a = 2;
console.log(obj1.a); // 输出 1在这个例子中,
Map
Map
默认情况下,
JSON.stringify()
structuredClone
对于函数,一种简单的处理方式是直接返回原始函数,这意味着拷贝后的对象和原始对象共享同一个函数。
function deepCopy(obj, map = new Map()) {
if (typeof obj !== 'object' || obj === null) {
return obj;
}
if (typeof obj === 'function') {
return obj; // 直接返回原始函数
}
if (map.has(obj)) {
return map.get(obj);
}
const newObj = Array.isArray(obj) ? [] : {};
map.set(obj, newObj);
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
newObj[key] = deepCopy(obj[key], map);
}
}
return newObj;
}
const originalObj = { a: 1, b: function() { console.log('hello'); } };
const deepCopyObj = deepCopy(originalObj);
deepCopyObj.b(); // 输出 hello,和原始对象共享同一个函数对于 Symbol 类型,一种处理方式是忽略它,不进行拷贝。另一种处理方式是使用
Symbol.keyFor()
Symbol()
function deepCopy(obj, map = new Map()) {
if (typeof obj !== 'object' || obj === null) {
return obj;
}
if (typeof obj === 'function') {
return obj;
}
if (typeof obj === 'symbol') {
const key = Symbol.keyFor(obj);
if (key) {
return Symbol.for(key); // 如果是全局 Symbol,重新创建
} else {
return Symbol(obj.description); // 如果是局部 Symbol,重新创建
}
}
if (map.has(obj)) {
return map.get(obj);
}
const newObj = Array.isArray(obj) ? [] : {};
map.set(obj, newObj);
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
newObj[key] = deepCopy(obj[key], map);
}
}
return newObj;
}
const sym = Symbol('test');
const originalObj = { a: 1, b: sym };
const deepCopyObj = deepCopy(originalObj);
console.log(deepCopyObj.b === sym); // 输出 false,重新创建了 Symbol需要注意的是,拷贝函数和 Symbol 类型可能会带来一些问题,例如,拷贝后的函数可能无法访问原始对象的作用域,拷贝后的 Symbol 可能不再是唯一的。因此,需要根据具体情况选择合适的处理方式。
以上就是javascript怎么实现数组深拷贝的详细内容,更多请关注php中文网其它相关文章!
java怎么学习?java怎么入门?java在哪学?java怎么学才快?不用担心,这里为大家提供了java速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号