欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 健康 > 养生 > 拷贝和浅拷贝的区别,以及对于循环引用如何处理深拷贝

拷贝和浅拷贝的区别,以及对于循环引用如何处理深拷贝

2025/5/8 3:04:04 来源:https://blog.csdn.net/yq_dxp1218/article/details/143747428  浏览:    关键词:拷贝和浅拷贝的区别,以及对于循环引用如何处理深拷贝

深拷贝和浅拷贝的区别,以及对于循环引用如何处理深拷贝

浅拷贝仅拷贝对象的第一层属性值,对于基本数据类型,会复制其值;对于引用数据类型,仅复制引用地址而不复制实际的对象内容。浅拷贝后的新对象与原对象中的引用类型属性仍指向同一个内存地址。

const obj1 = {name: "Alice",details: { age: 25 }
};
const obj2 = { ...obj1 }; // 浅拷贝
obj2.name = "Bob";
obj2.details.age = 30;
console.log(obj1.name);     // "Alice",浅拷贝不会影响原始对象的基本类型属性
console.log(obj1.details.age); // 30,但嵌套对象被引用,修改后会影响原对象

在这个例子中,obj2.detailsobj1.details 指向同一个对象,因此修改 obj2.details.age 会影响 obj1.details.age
``

实现浅拷贝的常见方法
  • Object.assign()
  • 扩展运算符 { ...obj }
  • Array.prototype.slice()Array.prototype.concat()(用于数组)

2. 深拷贝

深拷贝会递归地拷贝对象的每一层,生成一个与原始对象内容完全独立的新对象。深拷贝后的对象和原对象没有任何引用关系,对新对象的任何更改都不会影响原对象。

代码示例
const obj1 = {
name: "Alice",
details: { age: 25 }
};const obj2 = JSON.parse(JSON.stringify(obj1)); // 深拷贝obj2.name = "Bob";
obj2.details.age = 30;console.log(obj1.name);     // "Alice",深拷贝不会影响原始对象
console.log(obj1.details.age); // 25,深拷贝后,嵌套对象也被完全独立

在这个例子中,obj2obj1 的深拷贝,obj2.detailsobj1.details 指向不同的内存地址,因此修改 obj2.details.age 不会影响 obj1.details.age

实现深拷贝的方法

  • JSON.parse(JSON.stringify(obj))(有局限性,不适用于循环引用、特殊对象类型如函数、DateRegExpMapSetundefined等)
  • 递归拷贝方法
  • 使用第三方库(如 lodash.cloneDeep

以下是我自己实现的一个深copy 的方法(可以处理循环引用)

var obj = {a: 1,c: [1, 2, 3, {name: 'deep',type: {hello: ['hello1', 'hello2', 'hello3']}}]
};
obj.b = obj.a;
const deepCopy = () => {const hash = new WeakMap()return function copyInner(data) {if (typeof data !== 'object' || data === null) {return data}if (hash.has(data)) {return hash.get(data);}const copy = Array.isArray(data) ? [] : {};hash.set(data, copy);for (let key in data) {if (data.hasOwnProperty(key)) {copy[key] = copyInner(data[key], hash); // 递归拷贝}}return copy}
}
const datacopy = deepCopy();
const newdata = datacopy(obj)
newdata.c[2] = 'gogo';
newdata.c[3].type.hello[2] = 'gogo1'
console.log(newdata, obj);

以下是打印结果
在这里插入图片描述

在深拷贝中使用 const hash = new WeakMap() 是为了处理循环引用,同时提升深拷贝的性能和内存管理效率。

使用 WeakMap 的原因

  1. 处理循环引用

    • 如果对象内部存在循环引用,普通的递归拷贝会导致无限循环。例如,obj.b = obj 会使对象引用自身,直接递归调用会导致“超出调用堆栈”错误(Maximum call stack size exceeded)。
    • 通过 WeakMap 保存对象的引用关系,在递归过程中,如果发现某个对象已存在于 WeakMap 中,则直接返回该对象的拷贝,避免重复拷贝和无限递归。
  2. 防止重复拷贝

    • 对象可能在结构中多次出现,不使用 WeakMap 可能会导致冗余的深拷贝操作,浪费内存。
    • 使用 WeakMap 可以在深拷贝时为每个对象生成一个唯一的拷贝引用,提升性能。
  3. 自动垃圾回收

    • WeakMap 对键值的引用是“弱引用”,这意味着一旦键不再被其他引用,它可以被垃圾回收,从而自动释放内存。

    • 使用 WeakMap 可以在完成深拷贝后确保那些不再使用的对象能够被自动回收,优化内存管理。

虽然 JSON.stringifyJSON.parse 来实现深拷贝的方式虽然简单高效,但它有一些显著的缺点和局限性,主要体现在以下几个方面:

1. 无法拷贝函数

  • JSON.stringify 会忽略对象中的函数属性。因此,如果对象包含函数,使用这种方法深拷贝时会丢失这些函数。
const obj = { name: "Alice", greet:function () { console.log("Hello!"); } 
}; 
const copy = JSON.parse(JSON.stringify(obj)); 
console.log(copy.greet); // undefined

2. 无法拷贝特殊对象类型

  • JSON.stringify 只支持 JavaScript 的基本数据类型(如字符串、数字、数组、普通对象等),不支持一些特殊对象类型,如 DateRegExpMapSetundefined 等。
  • 拷贝后的 Date 对象会变成字符串,而 RegExp 对象会变成空对象。
const obj = { date: new Date(), regex: /test/i };
const copy = JSON.parse(JSON.stringify(obj)); 
console.log(copy.date); // 转成了字符串 
console.log(copy.regex); // {}

3. 无法处理循环引用

  • JSON.stringify 不能序列化具有循环引用的对象,直接使用会导致抛出 TypeError 错误。
const obj = {};
obj.self = obj; // 循环引用
JSON.stringify(obj); // TypeError: Converting circular structure to JSON

4. 拷贝数据可能不准确

  • JSON.stringifyNaNInfinity-Infinity 的处理会将它们转换为 null
  • 类似地,undefined 属性会被忽略掉,这会导致拷贝的对象缺失原有的数据。
const obj = {value: NaN,infinity: Infinity,undefinedValue: undefined
};
const copy = JSON.parse(JSON.stringify(obj));
console.log(copy); // { value: null, infinity: null }

5. 性能问题

  • JSON.stringify 在处理大对象时性能会有影响,因为它会将整个对象转换为字符串,这对处理复杂的嵌套结构会有一定的性能损耗。

总结

由于这些局限性,JSON.stringifyJSON.parse 的深拷贝方法只适合用于结构简单的对象和数组,不包含函数、循环引用和特殊对象。如果要深拷贝复杂结构的对象,推荐使用递归或其他第三方库(如 lodash.cloneDeep)来实现。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词