浅拷贝 :浅拷贝创建一个新的数组或对象,其中的元素或属性是原数组或对象的引用。这意味着如果原数组或对象中的元素是对象或数组,那么新的数组或对象中的对应元素仍然是这些对象或数组的引用,而不是新的副本。
- 特点:
- 只复制一层,不会递归复制嵌套的对象或数组。
- 修改新数组或对象中的嵌套对象或数组会影响原数组或对象中的对应部分。
const a1 = [{ a: 1 }, { b: 2 }];
// 方法一
const a2 = [...a1];// 输出: [{ a: 1 }, { b: 2 }]
a2[0].a = 10;
console.log(a1); // 输出: [{ a: 10 }, { b: 2 }]
// 方法二
const a2 = Object.assign({}, a1);
a2[0].a = 3;
console.log(a1[0].a); // 输出: 3,说明是浅拷贝
//方法三
const a2= { ...a1};
a2[0].a = 3;
console.log(a1[0].a); // 输出: 3,说明是浅拷贝
深拷贝:深拷贝创建一个新的数组或对象,其中的元素或属性是原数组或对象的完全独立副本。这意味着即使原数组或对象中的元素是对象或数组,新的数组或对象中的对应元素也是新的对象或数组,而不是引用。
- 特点:
- 递归复制所有层次的嵌套对象或数组。
- 修改新数组或对象中的嵌套对象或数组不会影响原数组或对象中的对应部分。
const a1= { a: 1, b: { c: 2 } };
//方法一
const a2 = JSON.parse(JSON.stringify(a1));
a2.a = 10;
console.log(a1); // { a: 1, b: { c: 2 } };
//方法二 递归拷贝函数
//这种方法可以处理JSON方法的限制,包括函数、undefined、NaN、Infinity等特殊值,以及循环引用。
function deepClone(obj: any): any {
if (obj === null || typeof obj !== 'object') {
return obj;
}
if (Array.isArray(obj)) {
const copy = [] as any[];
for (let i = 0; i < obj.length; i++) {
copy[i] = deepClone(obj[i]);
}
return copy;
}
const copy = {} as any;
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
copy[key] = deepClone(obj[key]);
}
}
return copy;
}
const a2= deepClone(a1);
a2.a = 3;
console.log(a1); // { a: 1, b: { c: 2 } };
//方法三 使用 lodash 库
const _ = require('lodash');
const a2 = _.cloneDeep(a1);
a2.a = 3;
console.log(a1); // { a: 1, b: { c: 2 } };
//方法四 使用 structuredClone
//structuredClone 是一个内置的浏览器 API,可以用于深拷贝对象。它支持更多的数据类型,但不支持循环引用。
const a2= structuredClone(a1);
a2.a = 3;
console.log(a1); // { a: 1, b: { c: 2 } };
//方法五 使用第三方库 clone-deep
//clone-deep 是一个轻量级的库,专门用于深拷贝。
const cloneDeep = require('clone-deep');
const a2= cloneDeep(a1);
a2.a = 3;
console.log(a1); // { a: 1, b: { c: 2 } };
总结
- 浅拷贝:只复制一层,嵌套对象或数组仍然共享引用,简单来说就是引用内存地址是一样的。
- 深拷贝:递归复制所有层次的嵌套对象或数组,完全独立。简单来说就是引用内存地址是不一样的。
选择哪种拷贝方式取决于你的具体需求。如果你只需要复制一层,浅拷贝就足够了。如果你需要完全独立的副本,特别是当对象或数组中有嵌套结构时,应该使用深拷贝。