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

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

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

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)来实现。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/14725.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

gitlab与jenkins

一 gitlab代码仓库 1.1 gitlab简介 GitLab 是一个用于仓库管理系统的开源项目,使用 Git 作为代码管理工具,并在此基础上搭建起来的 web 服务。GitLab 具有很多功能,比如代码托管、持续集成和持续部署(CI/CD)、问题跟踪…

LeetCode 86.分隔链表

题目: 给你一个链表的头节点 head 和一个特定值 x ,请你对链表进行分隔,使得所有 小于 x 的节点都出现在 大于或等于 x 的节点之前。 你应当 保留 两个分区中每个节点的初始相对位置。 思路: 代码: /*** Definiti…

Qt/C++ 开源控件 可折叠的标签管理控件

在 Qt 开发中,许多项目需要处理标签管理功能,例如分类管理、标签筛选等需求。本文将分享如何利用 Qt/C 实现一个具备动态增删标签、展开折叠功能的控件。此控件由 TagWindow 和 TagItemWidget 两个类组成,前者负责整个标签管理窗口的布局与逻…

Jmeter中的监听器(三)

9--断言结果 功能特点 显示断言结果:列出所有断言的结果,包括通过和失败的断言。详细信息:显示每个断言的详细信息,如断言类型、实际结果和期望结果。错误信息:显示断言失败时的错误信息,帮助调试。颜色编…

七牛云上传图片成功,但是无法访问显示{error : document not found}

上传图片成功,但是访问不了的问题,直接把地址放进浏览器显示{error : document not found},直接访问 DCNF 404是符合预期的,因为还没有去空间复制外链,要访问实际存在的资源才可以的. 配置区域和访问域名 设置没问题了…

虚拟与现实交融,线上元宇宙会议应用场景有哪些?

随着科技的飞速发展,元宇宙技术正逐渐渗透到我们生活的各个领域,为企业会议、学术会议、行业展会以及文化娱乐等带来了前所未有的变革。线上元宇宙会议打破了地域和物理空间的限制,让人们能够在虚拟世界中实现跨时空的交互与合作。本文将深入…

构建高效在线商店:Spring Boot框架应用

1 绪论 1.1 研究背景 当前社会各行业领域竞争压力非常大,随着当前时代的信息化,科学化发展,让社会各行业领域都争相使用新的信息技术,对行业内的各种相关数据进行科学化,规范化管理。这样的大环境让那些止步不前&#…

鸿蒙网络编程系列47-仓颉版UDP客户端

1. UDP通讯简介 本系列的第1篇文章《鸿蒙网络编程系列1-UDP通讯示例》中基于ArkTS语言在API 9的环境下演示了UDP通讯的基础用法,本文将使用仓颉语言在API 12的环境中实现类似的功能。这可能听起来有点不太现实,在ArkTS语言下可以利用kit.NetworkKit下的…

Redis与IO多路复用

1. Redis与IO多路复用概述 1.1 Redis的单线程特性 Redis是一个高性能的键值存储系统,其核心优势之一便是单线程架构。在Redis 6.0之前,其所有网络IO和键值对的读写操作都是由一个主线程顺序串行处理的。这种设计简化了多线程编程中的锁和同步问题&…

HarmonyOS Next 组件或页面之间的所有通信(传参)方法总结

系列文章目录 【鸿蒙】HarmonyOS NEXT开发快速入门教程之ArkTS语法装饰器(上) 【鸿蒙】HarmonyOS NEXT开发快速入门教程之ArkTS语法装饰器(下) 【鸿蒙】HarmonyOS NEXT应用开发快速入门教程之布局篇(上) 【…

API接口:助力汽车管理与安全应用

随着汽车行业的飞速发展,越来越多的汽车管理技术被应用到交通安全和智慧交通系统中。在这一过程中,API接口起到了至关重要的作用。通过API接口,我们可以实现诸如车主身份验核、车辆信息查询等功能,从而为汽车智慧交通发展与安全应…

C哈的刷题计划之输出数字螺旋矩阵(1)

1、盲听C哈说 都说数据结构与算法是编程的核心,它们两个是内功与心法😀,其它编程工具只是招式,学会了内功与心法,学习新事物(这里特指层出不穷的IT技术)就没有那么难了,实际上&#…

AD22Duplicate Net Names Wire问题

在验证的时候发现报了这个错误 我这个原理图都是用自定义的元件 只写在name引脚名字是会报这个错的 但是换成designator引脚标识就不会了 建议是name引脚名字和designator引脚标识都写 写成一样都行,就不会报这个错了,别空着

centos7上安装mysql

1.现查看虚拟机上有没有wget包,如果没有的话进行安装 yum install -y wget 2.进入MySQL :: Download MySQL Yum Repository下载mysql安装源 找到与linux相应的版本,复制地址,如果找不到地址,可以复制如下 3.下载mysql官方yum源 …

hadoop报错找不到主类

错误: (base) mpsmps3:~$ hadoop hadoop_map_redce-1.0-SNAPSHOT.jar MovieDriver /input/movies-to-be-predicted.txt Error: Could not find or load main class hadoop_map_redce-1.0-SNAPSHOT.jar解决办法: 1.输入命令 hadoop classpath配置好了ha…

使用 start-local 脚本在本地运行 Elasticsearch

警告:请勿将这些说明用于生产部署 本页上的说明仅适用于本地开发。请勿将此配置用于生产部署,因为它不安全。请参阅部署选项以获取生产部署选项列表。 使用 start-local 脚本在 Docker 中快速设置 Elasticsearch 和 Kibana 以进行本地开发或测试。 此设…

Day14 - CV项目实战:SAR飞机检测识别

论文原文: ​​​​​​SAR-AIRcraft-1.0:高分辨率SAR飞机检测识别数据集 - 中国知网 第一排的7张图片,普通人肉眼很难看出对应的是第二排的飞机。 还有上图里标注的飞机,外行根本看不明白,为什么这些是,其他的不是。…

Threejs 材质贴图、光照和投影详解

1. 材质和贴图 材质(Material)定义了物体表面的外观,包括颜色、光泽度、透明度等。贴图(Textures)是应用于材质的图像,它们可以增加物体表面的细节和真实感。 1.1材质类型 MeshBasicMaterial&#xff1a…

笔记整理—linux驱动开发部分(11)中断上下文

触摸屏分为两种,一种为电阻式触摸屏,另一种为电容式触摸屏。电阻式触摸屏(x、x-、y、y-、AD)有两种接口,一种为SOC自带的接口(miscinput或platform),第二种为外部IC,通过…

网络编程示例之开发板测试

编译elf1_cmd_net程序 (一)设置交叉编译环境。 (二)查看elf1_cmd_net文件夹Makefile文件。查看当前编译规则,net_demo是编译整个工程,clean是清除工程。 (三)输入命令。 &#xff0…