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

news2024/10/6 4:06:08

文章目录

  • 一、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.coloradmin.cn/o/1418511.html

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

相关文章

python零散学习

文章目录 __name__和__main__关系变量/函数的命名规则(下划线)python:List列表、Tuple元组、Dic字典%占位符使用class相关python常见内置类属性self python内置函数isinstance(object,classtype)globals() 高级语法相关…

干货分享 | TSMaster 信号映射的配置方法

TSMaster信号映射模块可以将数据库变量映射为系统变量,经过映射后的系统变量就等同于数据库中的变量,该系统变量的读写操作就等同于读写数据库变量。其在系统软件中的位置如下图所示: 信号映射模块设计的目的,就是为了实现上层应用…

新建VM虚拟机-安装centOS7-连接finalshell调试

原文 这里有问题 首先进入/etc/sysconfig/network-scripts/目录 cd /etc/sysconfig/network-scripts/ 然后编辑文件 ifcfg-ens33 vi ifcfg-ens33

树--二叉树(C语言纯手凹)

目录 1.什么是树?(不深入,仅做了解) 2.树的表示方式 2.1孩子兄弟表示法(左孩子右兄弟) 2.2孩子表示法 2.3双亲表示法 3.什么是二叉树 4.二叉树分类 4.1满二叉树 4.2完全二叉树 4.3二叉搜索树&#x…

键盘上Ins键的作用

前几天编写文档时,发现一个问题:插入内容时,输入的字符将会覆盖光标位置后的字符。原来是按到了键盘上的 Ins键,解决方法是:再按一次 Ins键(Ins键如果独立作为一键时,否则使用 “Fn Ins”组合键…

如何搭建Nextcloud云存储网盘并实现无公网ip访问本地文件【内网穿透】

💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

GitHub Copilot 与 ChatGPT:哪种工具更适合软件开发?

GitHub Copilot 与 ChatGPT:哪种工具更适合软件开发? 比较 ChatGPT 与 GitHub Copilot编程语言功能性定制化训练数据上下文准确性 ChatGPT 与 GitHub Copilot:哪个更适合软件开发?常见问题解答: 不断发展的编程世界正在…

Python:变量和简单类型

2.1 单行注释和多行注释 为程序添加注释可以用来解释程序某些部分的作用和功能,提高程序的可读性。除此之外,注释也是调试程序的重要方式。在某些时候,我们不希望编译、执行程序中的某些代码,这时就可以将这些代码注释掉。 Pyth…

【Web前端实操19】商城官网_分析与顶部广告

本次实操主要是借用小米之前的网站来进行参考,达成网站静态页面开发的目的,而新学者想要一次性直接开发整个网站,肯定会很懵圈,因此,这个商城官网我会一部分一部分地进行拆分来写,最后合成整个界面。 本次…

使用机器学习算法检测交易中的异常行为

交易中的异常检测意味着识别交易或相关活动中的异常或意外模式。这些模式被称为异常或异常值,明显偏离预期规范,可能表明存在不规则或欺诈行为。 异常检测在各种业务中发挥着至关重要的作用,尤其是那些涉及金融交易、在线活动和安全敏感操作…

如何实现无公网ip远程SSH连接家中本地的树莓派

文章目录 如何通过 SSH 连接到树莓派步骤1. 在 Raspberry Pi 上启用 SSH步骤2. 查找树莓派的 IP 地址步骤3. SSH 到你的树莓派步骤 4. 在任何地点访问家中的树莓派4.1 安装 Cpolar4.2 cpolar进行token认证4.3 配置cpolar服务开机自启动4.4 查看映射到公网的隧道地址4.5 ssh公网…

【前端】快速掌握CSS-flex布局

文章目录 一、标准流二、浮动1. 基本使用2. 产品区域布局(1) HTML标签(2) CSS样式 3. 清除浮动(1) 场景搭建(2) 额外标签法(3) 单伪元素法(4) 双伪元素法(5) overflow法 三、Flex布局1. Flex组成2. 主轴对齐方式3. 侧轴对齐方式4. 修改主轴方向5. 弹性伸缩比6. 弹性盒子换行7. …

手机壳也能散热了?

作为一个玩了6年的王者荣耀玩家,手机发热真的很影响游戏体验!!游戏掉帧,性能下降很恼人,试过好几个散热工具,实际效果都不太好~ 自从入了Mate 60之后,看着这款微泵液冷壳毫无犹豫第…

day35WEB 攻防-通用漏洞XSS 跨站反射存储DOMBeef-XSS

目录 一,XSS 跨站-原理&分类&手法&探针 1、原理 2、分类 3、危害 二,反射型XSS 1,案例演示 三,存储型XSS 1,案例演示 四,DOM 型XSS 五,XSS 利用环境-XSS 平台&Beef-XS…

如何使用Docker部署火狐浏览器并实现无公网ip远程访问

文章目录 1. 部署Firefox2. 本地访问Firefox3. Linux安装Cpolar4. 配置Firefox公网地址5. 远程访问Firefox6. 固定Firefox公网地址7. 固定地址访问Firefox Firefox是一款免费开源的网页浏览器,由Mozilla基金会开发和维护。它是第一个成功挑战微软Internet Explorer浏…

【Linux】动态库和静态库——动态库和静态库的打包和使用、gcc编译、拷贝到系统默认的路径、建立软连接

文章目录 动态库和静态库1.静态库和动态库的介绍2.静态库的打包和使用2.1生成静态库2.2使用静态库的三种方式2.2.1gcc编译2.2.2拷贝到系统默认的路径2.2.3建立软连接 3.动态库的打包和使用3.1生成动态库3.2使用动态库3.3解决加载不到动态库的方法 动态库和静态库 1.静态库和动…

“坦克大战”项目设计文档

「坦克大战」项目的设计文档 功能设计 游戏部分 ​ 在存在 障碍物 的地图上,玩家需要操作一辆绿色的坦克与敌对的灰色坦克作战。 坦克 以及其发射的 炮弹 均会被障碍物所阻挡。玩家用键盘上的 wasd按键 控制坦克移动,用 鼠标 控制炮弹发射方向&#x…

删除倒数第N个结点---链表Oj

https://leetcode.cn/problems/remove-nth-node-from-end-of-list/description/?envType=study-plan-v2&envId=top-100-liked 1、常规解法 删除倒数第N个结点,那就是删除整数第(len - N + 1)个结点,我们只需要找到第len-N个结点,让它的next指向倒数第N个结点…

赛氪荣获“2023天津高新技术企业大会支持单位”

1月23日上午,2023天津市高新技术企业大会新闻发布会在天开高教科技园核心区综合服务中心召开,市高企协以及来自高校、企业、社会组织等80余人现场参会。 大会组委会秘书长张博航介绍到:“本次大会将实现自开办以来的多个首次,首次…

【刷题】 leetcode 面试题 08.05.递归乘法

递归乘法 1 题目描述2 思路一(返璞归真版)3 思路二(二进制乘法器版)4 思路三(变态版)Thanks♪(・ω・)ノ谢谢阅读下一篇文章见!!! 1 题目…