js数组/对象的深拷贝与浅拷贝


文章目录

  • 一、js中的深拷贝和浅拷贝
  • 二、浅拷贝
    • 1、Object.assign()
    • 2、利用es6扩展运算符(...)
  • 二、深拷贝
    • 1、JSON 序列化和反序列化
    • 2、js原生代码实现
    • 3、使用第三方库lodash等
  • 四、总结


一、js中的深拷贝和浅拷贝

在JS中,深拷贝和浅拷贝是针对对象(Object)和数组(Array)这类复杂数据类型复制时的概念。

  • 浅拷贝: 当进行浅拷贝时,只是将对象或数组的引用复制一份给新的变量。这意味着新旧变量指向的是同一个内存空间中的数据结构,修改其中一个变量会影响到另一个变量,因为它们实际上是共享同一份数据。

  • 深拷贝: 则是创建一个与原对象完全独立的对象副本,对于原对象内部所包含的所有层级的数据(包括嵌套的对象或数组),都会递归地进行复制,从而保证了新旧对象间不存在任何共享的引用。

简单来说,对拷贝后的数组或对象进行修改,会影响原数组或对象的就是浅拷贝,不会影响的就是深拷贝。

二、浅拷贝

1、Object.assign()

Object.assign() 基本用法:将一个或多个源对象的属性复制到目标对象,并返回目标对象。这一过程是浅拷贝的,即对于嵌套对象或数组,只是拷贝了引用而非创建新的对象。
示例:

const obj = {a: 1,b: {c: 2},d: ["e"]
};
const cloneObj = Object.assign({}, obj);
cloneObj.b.c = 3; // 修改拷贝后的对象console.log("obj: ", obj);
console.log("cloneObj: ", cloneObj);

结果:
在这里插入图片描述
从结果可以看到,修改 clonedObj 的属性也会影响到原始对象 obj,所以Object.assign()实现的是浅拷贝。

2、利用es6扩展运算符(…)

在ES6中,...运算符被称为扩展(Spread)运算符,可以用于数组和对象的浅拷贝。
示例1:拷贝对象

const obj2 = {a: 1,b: {c: 2},d: ["hello"]
};
const cloneObj2 = { ...obj2 };
cloneObj2.b.c = 2222222;console.log("obj2: ", obj2);
console.log("cloneObj2: ", cloneObj2);

结果:
在这里插入图片描述

示例2:拷贝数组

const arr = ["1", { a: 2 }, 3, true, undefined];
const arrClone = [...arr];
arrClone[0] = "first"; // 修改数组第1个元素
arrClone[1].a = "hello"; // 修改数组第2个元素console.log("arr: ", arr);
console.log("arrClone: ", arrClone);

结果:
在这里插入图片描述

...拷贝数组结果来看,原始数组和拷贝数组是独立的,但由于数组中包含的对象引用并未改变,所以修改拷贝数组中对象的属性时,原始数组中对应的对象也会受到影响。

也就是说,对于简单数组,...可以实现数组深拷贝,但是复杂数组…则不能用做实现深拷贝的方法。

二、深拷贝

1、JSON 序列化和反序列化

利用 JSON 的序列化和反序列化可以实现对象的深拷贝。原理是:JSON.stringify()会递归遍历对象的所有属性(包括嵌套的对象和数组),将其转换为JSON字符串;然后JSON.parse()则会根据JSON字符串创建新的JavaScript对象。

示例:

const obj1 = {a: 1,b: {c: 2},d: ["hello"]
};
const cloneObj1 = JSON.parse(JSON.stringify(obj1));
cloneObj1.b.c = 3;console.log("obj1: ", obj);
console.log("cloneObj1: ", cloneObj);

结果:
在这里插入图片描述
从结果看到,修改拷贝后的对象其中的属性值,并不会改变原对象的属性值,所以实现的是一个深拷贝。

!!!注意这种方法有局限:

  • 它不能处理函数和RegExp等非JSON兼容类型。
  • 对象中的循环引用会导致错误或丢失数据。
  • 如果对象中有undefined、function或symbol类型的属性,它们在序列化过程中会被忽略。
  • 对日期对象,JSON.stringify会将日期转换为字符串,所以反序列化后得到的是字符串而不是Date对象,如果需要保持日期类型,需要额外处理。

2、js原生代码实现

  • 简单版:只考虑普通对象属性,不考虑内置对象和函数
	function deepClone1(obj) {if (typeof obj !== "object") return;let newObj = obj instanceof Array ? [] : {};for (let key in obj) {if (obj.hasOwnProperty(key)) {newObj[key] =typeof obj[key] === "object" ? deepClone1(obj[key]) : obj[key];}}return newObj;}// 测试
const obj5 = {a: 1,b: {c: 2},d: ["hello"],e: new Date(),f: new Error("error"),g: new RegExp("/^(.*\..{4}).*$/")
};
const cloneObj5 = deepClone1(obj5);
cloneObj5.b.c = "hhhhhh";console.log("obj5: ", obj5);
console.log("cloneObj5: ", cloneObj5);

结果:
在这里插入图片描述

  • 【推荐】复杂版:考虑内置对象比如Date、RegExp等对象和函数以及解决循环引用的问题。
// 判断是否为object类型
function isObject(target) {return ((typeof target === "object" && target) || typeof target === "function");}
function deepClone(data, map = new WeakMap()) {// 基础类型直接返回值if (!isObject(data)) {return data;}// 日期或者正则对象则直接构造一个新的对象返回if ([Date, RegExp].includes(data.constructor)) {return new data.constructor(data);}// 处理函数对象if (typeof data === "function") {return new Function("return " + data.toString())();}// 如果该对象已存在,则直接返回该对象const exist = map.get(data);if (exist) {return exist;}// 处理Map对象if (data instanceof Map) {const result = new Map();map.set(data, result);data.forEach((val, key) => {// 注意:map中的值为object的话也得深拷贝if (isObject(val)) {result.set(key, deepClone(val));} else {result.set(key, val);}});return result;}// 处理Set对象if (data instanceof Set) {const result = new Set();map.set(data, result);data.forEach(val => {if (isObject(val)) {// 注意:set中的值为object的话也得深拷贝result.add(deepClone(val));} else {result.add(val);}});return result;}// 收集键名(考虑了以Symbol作为key以及不可枚举的属性)const keys = Reflect.ownKeys(data);// 利用 Object 的 getOwnPropertyDescriptors 方法可以获得对象的所有属性以及对应的属性描述const allDesc = Object.getOwnPropertyDescriptors(data);// 结合 Object 的 create 方法创建一个新对象,并继承传入原对象的原型链, 这里得到的result是对data的浅拷贝const result = Object.create(Object.getPrototypeOf(data), allDesc);// 新对象加入到map中,进行记录map.set(data, result);// Object.create()是浅拷贝,所以要判断并递归执行深拷贝keys.forEach(key => {const val = data[key];if (isObject(val)) {// 属性值为 对象类型 或 函数对象 的话也需要进行深拷贝result[key] = deepClone(val);} else {result[key] = val;}});return result;
}// 测试
const obj4 = {a: 1,b: {c: 2},d: ["hello"],e: new Date(),f: new Error("error"),g: new RegExp("/^(.*\..{4}).*$/")
};
const cloneObj4 = deepClone(obj4);
cloneObj4.b.c = "hhhhhh";console.log("obj4: ", obj4);
console.log("cloneObj4: ", cloneObj4);

结果:
在这里插入图片描述

3、使用第三方库lodash等

例如:import { cloneDeep } from 'lodash'; 这种拿来就可以用的方法就不做过多介绍了~

四、总结

总的来说,js中的深、浅拷贝其实就是一个基本功,在实际业务场景中,可根据数据复杂程度自行选择各种实现方式,加油⛽️

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

鸿蒙开发会是前端程序员的下一个春天吗?

前言 最近前端的大环境不太行,之前身处在前端的自己薪资也越来越无望了,隐隐约约感觉前端做不下去了,2024前端找不到工作要转行吗? 看新闻的过程中,**发现越来越多的巨头公司融入鸿蒙生态建设,鸿蒙“朋友…

【C++】入门

结束数据结构初阶的学习后,很高兴继续学习C,欢迎大家一起交流~ 目录 C关键字 命名空间 命名空间定义 命名空间使用 C输入&输出 缺省参数 缺省参数概念 缺省参数分类 函数重载 函数重载概念 C支持函数重载的原理--名字修饰 引用 引用概念…

【大根堆】【C++算法】871 最低加油次数

作者推荐 【动态规划】【map】【C算法】1289. 下降路径最小和 II 本文涉及知识点 大根堆 优先队列 LeetCode:871最低加油次数 汽车从起点出发驶向目的地,该目的地位于出发位置东面 target 英里处。 沿途有加油站,用数组 stations 表示。其中 statio…

用4KB内存寻找重复元素(算法村第十五关青铜挑战)

在海量数据中,普通的数组、链表、Hash、树等等结构无效,因为内存空间不足。而常规的递归、排序,回溯、贪心和动态规划等思想也无效,因为执行超时。这类问题该如何下手呢?这里介绍三种非常典型的思路: 1.使…

如何在Vue项目中应用TypeScript?

文章目录 一、前言二、使用Componentcomputed、data、methodspropswatchemit 三 、总结 一、前言 与link类似 在VUE项目中应用typescript,我们需要引入一个库vue-property-decorator, 其是基于vue-class-component库而来,这个库vue官方推出…

Caused by: com.mongodb.MongoTimeoutException: Timed out after 30000 ms

报错 Caused by: com.mongodb.MongoTimeoutException: Timed out after 30000 ms while waiting to connect. Client view of cluster state is {typeUNKNOWN, servers[{addressmangodb-m.cc.com:3717, typeUNKNOWN, stateCONNECTING, exception{com.mongodb.MongoSocketReadE…

uniapp 使用echarts做折线图条形图。

提前10天把中烟活动做完了,以为能打酱油到除夕那天,结果又要做什么数据看板,方便烟草领导过年查看数据,还只给5天时间,真实压榨剥削啊,下辈子再也不‘拍黄片’了,不!下份工作我就转前…

操作系统--Linux虚拟内存管理

​一、什么是虚拟内存地址 收货地址是一个虚拟地址,它是人为定义的 而我们的城市,小区,街道是真实存在的,他们的地理位置就是物理地址 以 Intel Core i7 处理器为例,64 位和32位虚拟地址的格式为: 二、为什…

Android App开发基础(3)——App的设计规范

3 App的设计规范 本节介绍了App工程的源码设计规范,首先App将看得见的界面设计与看不见的代码逻辑区分开,然后利用XML标记描绘应用界面,同时使用Java代码书写程序逻辑,从而形成App前后端分离的设计规约,有利于提高App集…

Fastbee开源物联网项目RoadMap

架构优化 代码简化业务&协议解耦关键组件支持横向拓展网络协议支持横向拓展,包括:mqtt broker,tcp,coap,udp,sip等协议插件化编码脚本化业务代码模版化消息总线 功能优化 网关/子网关:上线,绑定,拓扑&#xff0…

快毕业了,同学纪念册如何制作出高级感

​快毕业了,这是一个充满回忆和感慨的时刻。同学们都想制作一本高级感的同学纪念册,留住这段美好的时光。但是自己着手制作的纪念册太丑,那不出手怎么办?那你就问对人了,我给大家演示几个步骤,需要的可以学…

【java】常见的面试问题

目录 一、异常 1、 throw 和 throws 的区别? 2、 final、finally、finalize 有什么区别? 3、try-catch-finally 中哪个部分可以省略? 4、try-catch-finally 中,如果 catch 中 return 了,finally 还会执行吗&#…

ambari hdp 企业级安装实战

一 配置部署环境 1.1 检查操作系统 1.1.1 检查操作系统版本 cat /etc/redhat-release1.1.2 检查操作系统默认语言 $ echo $LANG en_US.UTF-8若操作系统默认语言为en_US.UTF-8,则进入下一步。 若操作系统默认语言非en_US.UTF-8,则执行以下步骤: # 将默认语言由中文切换为…

瀑布流布局 (初版)

瀑布流布局 文章目录 瀑布流布局前言1. 背景2. 点⬇️🔗去体验效果如下图所示: 一、初版waterfall布局和问题暴露?1.效果图如下:2.暴露问题如下图所示:第一张问题图:第二张问题图: 3.HTML代码如…

有效的字母异位词

42. 有效的字母异位词https://leetcode.cn/problems/valid-anagram/ 给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的字母异位词。 注意:若 s 和 t 中每个字符出现的次数都相同,则称 s 和 t 互为字母异位词。 示例 1: 输入: s …

go语言Map与结构体

1. Map map是一种无序的基于key-value的数据结构,Go语言中的map是引用类型,必须初始化才能使用。 1.1. map定义 Go语言中 map的定义语法如下 map[KeyType]ValueType其中, KeyType:表示键的类型。ValueType:表示键对应的值的类型。map类型的…

maven组件升级报错经验汇总

1. NosuchMethodError org.springframework.beans.factory.support.genericBeanDefinition(xxxxx) 2. ClassNotFoundException: org.springframework.boot.SpringApplication 可能冲突的依赖是&#xff1a; <dependency><groupId>org.springframework.boot</g…

NodeJS Express实现所有页面Http访问重定向跳转为Https

要在Node.js Express中实现所有页面从HTTP访问跳转到HTTPS&#xff0c;你可以使用重定向中间件。以下是一个简单的示例&#xff1a; 1. 首先&#xff0c;确保你已经安装了Express和express-redirect中间件。如果没有&#xff0c;你可以通过npm进行安装&#xff1a; npm insta…

8.6 代理设计模式

文章目录 一、代理模式&#xff08;Proxy Pattern&#xff09;概述二、代理模式和观察者设计模式三、模式结构四、协作角色五、实现策略六、相关模式七、示例八、应用 一、代理模式&#xff08;Proxy Pattern&#xff09;概述 代理模式是一种设计模式&#xff0c;它通过引入一个…

智能体AI Agent的极速入门:从ReAct到AutoGPT、QwenAgent、XAgent

前言 如这两天在微博上所说&#xff0c;除了已经在七月官网上线的AIGC模特生成系统外&#xff0c;我正在并行带多个项目组 第二项目组&#xff0c;论文审稿GPT第2版的效果已经超过了GPT4&#xff0c;详见《七月论文审稿GPT第2版&#xff1a;用一万多条paper-review数据集微调…