js中的深拷贝与浅拷贝 手写深拷贝代码

1 什么是深拷贝和浅拷贝?

        深拷贝和浅拷贝都是复制对象时常用的两种方式,区别在于对于嵌套对象的处理,浅拷贝只复制属性的第一层属性,双方修改嵌套对象将会互相影响。深拷贝会递归复制每一层的属性,修改任意一方互不影响。

2 浅拷贝和浅拷贝的实现方法

2.1 浅拷贝的实现方法

        1.使用Object.assign()方法,接收两个参数(target, source),将source上的第一层属性复制到target的第一层属性上(不改变原有属性,如果重名将会被覆盖,常用于配置对象的修改)。

// Object.assign
// Object.assign() copies property values from a source object to a target object. For example, consider the following code:
const source = { b: 4, c: {d : 9} };
const shallowCopy = Object.assign({}, source);shallowCopy.c.d = 10;
console.log(source); // { b: 4, c: { d: 10 } }

         2. 使用展开运算符赋值

// use ... (spread operator) for shallow copy
const source = { b: 4, c: {d : 9} };
const shallowCopy = {...source};shallowCopy.c.d = 10;
console.log(source); // { b: 4, c: { d: 10 } }

        3. 对数组可以使用slice或者concat方法

// Array use slice or concat for shallow copy
const source = [1, 2, 3];
const shallowCopy_01 = source.slice();
// or
const shallowCopy_02 = source.concat();
shallowCopy_01[0] = 100;
shallowCopy_02[0] = 100;
console.log(source); // [1, 2, 3]

2.2 深拷贝的实现方法-包含手写实现

        方法一:使用递归手写实现,具体实现步骤如下所示:

        1. 处理基本类型和引用类型

function deepClone(value){// 处理基本类型(非引用类型)和nullif(typeof value !== 'object' || value === null){return value}
}

        2.  处理日期Date对象和正则RegExp对象

// 处理正则对象
function deepClone(value){// ...上一步代码// Date对象和正则对象直接通过new的方式创建一个新的对象并返回if(value instanceof RegExp){return new RegExp(value)}if(value instanceof Date){return new Date(value)}
}

         3. 处理函数(函数一般不进行深拷贝,闭包所设计的引用过于复杂)

function deepClone(value){//...前面代码    // 处理函数 直接返回,不做处理if(typeof value === 'function'){return value}
}

        4.  初始化拷贝对象

// 初始化拷贝对象
function deepClone(value){// ...前面代码// 初始化拷贝对象const copy = Array.isArray(value) ? [] : {}
}

        5.  处理循环引用:使用WeakMap记录拷贝过的对象,防止循环引用

// 处理循环引用
function deepClone(value, cache = new WeakMap()){//...前面代码// 如果拷贝过的对象,直接返回if(cache.get(value)){return cache.get(value)}// 缓存拷贝对象cache.set(value, copy)
}

        6.  递归拷贝对象的属性:使用 Reflect.ownKeys 获取对象的所有属性,包括不可枚举属性和 Symbol 属性,然后递归地拷贝每个属性:

// 递归拷贝对象属性
function deepClone(value, cache = new WeakMap()){// ...上面的代码// 递归拷贝对象属性const keys = Reflect.ownKeys(value)for(let key of keys){copy[key] = deepClone(value[key], cache)}return copy
}

        7. 处理 Map和Set

function deepClone(value, cache = new WeakMap()){// ...上面代码// 缓存拷贝对象cache.set(value, copy)// 处理Map和Setif(value instanceof Map){const copy = new Map()value.forEach((val, key) => {copy.set(key, deepClone(val, cache))})return copy}if(value instanceof Set){const copy = new Set()value.forEach(val => {copy.add(deepClone(val, cache))})return copy}// ...后续代码
}
手写深拷贝完整代码
function deepClone(value, cache = new WeakMap()){// 处理基本类型(非引用类型)和null,直接返回即可if(typeof value !== 'object' || value === null){return value}// Date对象和正则对象直接通过new的方式创建一个新的对象并返回if(value instanceof RegExp){return new RegExp(value)}if(value instanceof Date){return new Date(value)}// 处理函数 直接返回,不做处理if(typeof value === 'function'){return value}// 初始化拷贝对象(保留对象原型链)const copy = Array.isArray(value) ? [] : Object.create(Object.getPrototypeOf(value))// 如果拷贝过的对象,直接返回if(cache.get(value)){return cache.get(value)}// 缓存拷贝对象cache.set(value, copy)// 处理Map和Setif(value instanceof Map){const copy = new Map()value.forEach((val, key) => {copy.set(key, deepClone(val, cache))})return copy}if(value instanceof Set){const copy = new Set()value.forEach(val => {copy.add(deepClone(val, cache))})return copy}// 递归拷贝对象属性(包含不可枚举属性)const keys = Reflect.ownKeys(value)for(let key of keys){copy[key] = deepClone(value[key], cache)}return copy
}

         方法二:使用JSON.parse(JSON.stringify(obj)),但是会造成部分数据丢失,且无法处理特殊对象。

// deep copt use Json.parse and Json.stringify
const source = { b: 4, c: {d : 9} };
const deepCopy = JSON.parse(JSON.stringify(source));
深拷贝测试示例
const obj = {num: 1,str: 'string',bool: true,nullValue: null,undefinedValue: undefined,symbol: Symbol('sym'),date: new Date(),regExp: /\w+/g,func: function () { console.log('function'); },arr: [1, 2, { a: 3 }],obj: { x: 10, y: { z: 20 } },map: new Map([['key1', 'value1'], ['key2', { a: 1 }]]),set: new Set([1, 2, { b: 3 }]),[Symbol('symbolKey')]: 'symbolValue',
};obj.circularRef = obj; // 添加循环引用const clonedObj = deepClone(obj);console.log(clonedObj);

        测试结果如下所示:

 

3 深拷贝需要注意的问题

        实现深拷贝时需要考虑以下问题:

  • 循环引用问题:如果对象内部引用自己,不考虑该情况将会导致无限递归。
  • 性能损耗:深拷贝大型对象和数组将会浪费大量性能,带来卡顿。
  • 考虑特殊对象类型:例如Date RegExp Set Map Function等对象需要特殊处理
  • 不可枚举属性和原型链:只会复制对象的可枚举属性
  • 某些深拷贝造成的数据丢失:使用JSON.parse(JSON.stringify(obj))时造成undefined Symbol Funcion等类型数据的丢失

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

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

相关文章

谷歌地图 | 3D 地图新功能:开发更简单,体验更丰富

今年早些时候在 Google I/O 大会上推出了地图 JavaScript API 中的逼真 3D 地图。从那时起,谷歌地图一直受到大家对 3D 地图的热烈反响,并从中汲取了大量灵感。9月25日,谷歌地图宣布实验性 3D 地图迎来了重大更新,这将使开发者更轻…

深度学习模型可视化工具 Netron 使用教程

Netron 介绍 Netron 是一个用于可视化机器学习模型、深度学习模型、神经网络、图模型(例如用于计算机视觉的 ONNX、Caffe、TensorFlow Lite、TensorFlow.js、Keras、Darknet、TVM、PyTorch、TorchScript、Core ML、ML.NET、NNEF、PaddlePaddle、OpenVINO、Arm NN等…

2024年9月25日--- Spring-IOC 1

一 Spring的概要 1.1 简介 Spring,春天的意思,意指给软件行业带来春天。2002年,Rod Jahnson首次推出了Spring框架雏形interface21框架。2004年3月24日,Spring框架以interface21框架为基础,经过重新设计,发…

广州数字孪生工业互联网可视化技术,赋能新型工业化智能制造工厂

在广州,特别是在工业互联网领域,数字孪生技术正逐步成为赋能新型工业化智能制造工厂的重要驱动力。数字孪生工业互联网技术的引入,不仅为传统制造业带来前所未有的变革,更为广州的工业发展注入新的活力与可能。 在智能制造工厂的…

Linux 基础IO(个人笔记)

Linux基础 IO 1.C文件IO操作1.1 hello.c写文件1.2 hello.c读文件1.3 stdin&stdout&stderr 2.系统文件I/O2.1 hello.c写文件2.2 hello.c读文件2.3 open函数介绍2.4 文件描述符 fd2.4.1 文件描述符的分配规则2.4.2 重定向2.4.3 dup2系统调用2.4.4 C文件结构体FILE2.4.5 C…

了解输出电源优先级

主要又SUB,SBU以及USB三种模式。 调试10kW逆变器存在的输出电源优先级的问题,当优先级为SUB时,利用电压源模拟电池,当电池电压超过58.4V,即过压,在接入市电,市电继电器仍然闭合,仍然…

使用kubectl快速查看各个节点的CPU和内存占用量

本文章视频教程地址:https://www.bilibili.com/video/BV1TdxkedE1K 前言 笔者之前写过一篇文章关于在Kubernetes上安装 Prometheus 和 Grafana 监控去查看Kubernetes各个节点的资源占用率,文章地址:https://blog.csdn.net/m0_51510236/arti…

大模型(LLM) 是仅仅比 模型(Model) 更大吗?

我们日常经常提到模型 model,大模型LLM,大家都知道这两者之间的区别和联系吗? 只是如下图这样,大小的区别吗?下面我们对模型model和大模型LLM进行解释和描述 什么是模型? 模型是机器学习中一个核心概念&a…

matlab2019b-2024b knnclassify无法识别的问题(亲测,已解决)

matlab2019a-2024b 已经移除了knnclassify分类,修改了名称和功能,如果你还想使用它,就必须在2018版本以前的旧版本中找相关的工具箱(这是免费的哦,如果官网下载 需要付费)。 这里本人从2014a中分离出的工具…

JS设计模式之观察者模式:观察者与可观察对象的巧妙互动

一. 前言 在前端开发中,我们经常会遇到需要对用户的操作进行响应的场景,例如页面上的按钮点击、输入框内容变化等。为了实现这种响应式的设计,我们可以使用观察者模式来解耦各个组件之间的依赖关系。 本文将详细介绍观察者模式的原理和实现…

使用【apifox】进行压测-保姆级教程【无需脚本】

1.根据接口文档进行测试,写一个接口,能够调通即可 2.选择“从接口导入”,选择刚刚测试的接口 3.选择一个环境,我这里用的云服务器http://x.xx.xxx.xx (端口号写不写都行,我是加上了) 4.选性…

element-ui 通过按钮式触发日期选择器

element ui 写在前面1. 自定义的日期时间组件CustomDatePicker.vue2. 页面效果总结写在最后 写在前面 需求:elementui中日期时间选择器,目前只能通过点击input输入框触发日期选择器,我希望能通过其他方式触发日期选择器同时把input输入框去掉…

【IoT-NTN】系统消息SIB32信令分析

3GPP卫星通信发展迅速, TS36.331 R17中新增SIB32携带星历信息,本文对SIB32的信令内容进行了分析。 SystemInformationBlockType32 概述 SystemInformationBlockType32 是用于提供预测非连续覆盖的卫星辅助信息的系统信息块。这个信息块仅在非地面网络&…

初学者如何快速入门Python(详细攻略),从0到精通,不信你学不会!

近年来,人工智能领域的飞速发展极大地改变了各个行业的面貌。当前最新的技术动态,如大型语言模型和深度学习技术的发展,展示了深度学习和机器学习技术的强大潜力,成为推动创新和提升竞争力的关键。特别是PyTorch,凭借其…

刚面试完的前端面试题

今天晚上参加了一场长达40多分钟的技术面。我觉得面试官非常专业,问的问题也都是很棒的!自己很多知识都需要学习。所以我决定回想并记录下来。回答不对的地方欢迎大家指正! 我自己在小本本上回忆出来的大概就是26道题。后期我会持续更新我学习…

【测试-BUG篇】软件测试的BUG知识你了解多少呢?

文章目录 1. 软件测试的生命周期2. BUG3. BUG的生命周期4. 与开发人员起争执怎么办 1. 软件测试的生命周期 🍎软件测试 贯穿整个软件的生命周期; 🍎软件测试的生命周期是指测试流程; ①需求分析 用户角度:软件需求是…

资源管理5步法:优化你的项目管理

作为项目经理,负责交付项目是一项复杂且要求严格的任务。在缺乏必要的专业知识、工具、设备以及资金支持的情况下,成功完成项目几乎是不可能的。 因此,项目资源的规划与分配是项目启动前至关重要的环节。若未能妥善规划,将可能导…

测试卡(1)灰卡

#灵感# 灰卡为什么是18%?文章分为三部分,前部分,解释灰卡的定义,后部分是 市场买的18%灰卡的说明书,其中穿插了网络上搜到的灰卡使用案例。 目录 18% 中性灰卡应用说明 1) 曝光水平 例子:用灰…

有些硬盘录像机接入视频汇聚平台EasyCVR后通道不显示/显示不全,该如何处理?

EasyCVR视频监控汇聚管理平台是一款针对大中型项目设计的跨区域网络化视频监控集中管理平台。该平台不仅具备视频资源管理、设备管理、用户管理、运维管理和安全管理等功能,还支持多种主流标准协议,如GB28181、RTSP/Onvif、RTMP、部标JT808、GA/T 1400协…

初识Linux以及Linux的基本命令

千呼万唤始出来,Linux系列的文章从今天起开始不定期更新,闲话少叙,我们直接进入正题 初识Linux 这一部分我不打算给大家讲Linux的发展史啥的,直接从系统方面开始介绍 首先,我们平时用win10或win11所看到的桌面以及各…