理解浅拷贝和深拷贝以及实现方法

news2025/1/12 20:36:47

一、数据类型

数据分为基本数据类型(String, Number, Boolean, Null, Undefined,Symbol)和引用数据类型Object,包含(function,Array,Date)。

1、基本数据类型的特点:直接存储在栈内存中的数据
2、引用数据类型的特点:存储的是该对象在栈中引用,真实的数据存放在堆内存里

引用数据类型在栈中存储了指针,该指针指向堆中该实体的起始地址。当解释器寻找引用值时,会首先检索其在栈中的地址,取得地址后从堆中获得实体。
在这里插入图片描述

二、浅析栈和堆

通过栈里面定义一个地址值,通过地址值去找堆里面定义的某一个值,
很重要一点是他的栈里是个地址值,地址值指向的是堆,他在堆里面定义的某一个值
相当于拿着号,去堆里面去找,两个号也就是地址值其实是一模一样的

堆跟栈最大的区别是:
1)堆在栈里存了一个地址值
2)栈存储的永远是一个基本数据类型的数据

三、对深浅拷贝理解

浅拷贝:创建一个新对象,这个对象有着原始对象属性值的一份精准拷贝。如果属性是基本类型,拷贝的就是基本类型的值;如果属性是引用类型,拷贝的就是内存地址。这个内存地址指向同一个堆内存。如果其中一个对象改变了这个地址,就会影响到另一个对象。

深拷贝:创建一个新对象,如果属性是基本类型,拷贝的就是基本类型的值;如果属性是引用类型,则从堆内存中开辟一个新的区域存放该引用类型指向的堆内存中的值,修改新对象的值不会影响原对象。

浅拷贝和深拷贝引用其他博主图片,示意图大致如下:

在这里插入图片描述

在这里插入图片描述
总之,浅拷贝只复制指向某个对象的指针,而不复制对象本身,新旧对象还是共享同一块内存。但深拷贝会另外创造一个一模一样的对象,新对象跟原对象不共享内存,修改新对象不会修改到原对象。

二. 浅拷贝实现方法

1)ES6语法展开运算符…

    const obj1 = {
      name: "icy",
      age: 20,
      hobbies: ["eat", "sleep", "game"],
    };
    const obj2 = { ...obj1 }
    console.log(obj2, 'obj2')

打印结果
在这里插入图片描述
为了验证是浅拷贝,我们改变一下obj2中数组的第一项的值,然后再输出ojb1和obj2:

const obj1 = {
      name: 'Nancy',
      age: 18,
      hobbies: ['eat', 'sleep', 'game']
    }
    const obj2 = { ...obj1 }

    // 修改堆内存中的值
    obj2.age = 20
    obj2.hobbies[0] = 'play'

    console.log('修改后obj2', obj2)
    console.log('修改前obj1', obj1)

打印结果如下:

在这里插入图片描述

修改数组时候,obj1和obj2都受到了影响,验证了浅拷贝。

2)Object.assign() 方法

Object.assign() 方法可以把任意多个的源对象自身的可枚举属性拷贝给目标对象,然后返回目标对象,返回值为合并后的新对象。但是Object.assign() 进行的是浅拷贝,拷贝的是对象的属性的引用,而不是对象本身。

注意: 当object只有一层的时候,是深拷贝。

  let obj = { username: 'kobe' }
  let obj2 = Object.assign({}, obj)   //将拷贝对象与{}空对象合并
  obj2.username = 'wade'
  
  console.log(obj,'obj')
  console.log(obj2,'obj2')

在这里插入图片描述

3)Array.prototype.concat() 方法

该方法用于数组合并,合并的数组.concat(被合并的数组…)
参数可有多个,用逗号分隔,返回合并后的数组。
原理:用原数组去合并一个空数组,返回合并后的数组。

let arr = [1, 3, {
   username: 'kobe'
}];
let arr2 = arr.concat();    
arr2[2].username = 'wade';
arr2[0] = 555;

console.log(arr,'arr');
console.log(arr2,'arr2');

在这里插入图片描述

4)数组剪裁方法 slice()

该方法用途很多,可对数组进行增删,剪裁操作。

 const arr1 = [1, 3, { username: 'kobe' }]
 const arr2 = arr1.slice()  //返回剪裁后的数组,这里没有剪裁掉任何项,相当于返回原数组

 // 修改堆内存中的值
 arr2[0] = 5
 arr2[2].username = 'wade'

 console.log('arr1', arr1)
 console.log('arr2', arr2)

在这里插入图片描述

关于Array的slice和concat方法的补充说明:Array的slice和concat方法不修改原数组,只会返回一个浅复制了原数组中的元素的一个新数组。

个人感觉,以上四种方法都只有一层的时候才是深拷贝。

5)手写浅拷贝

创建一个新的对象,遍历需要克隆的对象,将需要克隆对象的属性依次添加到新对象上,返回。

function clone(target) {
    let cloneTarget = {};
    for (const key in target) {
        cloneTarget[key] = target[key];
    }
    return cloneTarget;
};

三. 深拷贝实现方法

1)JSON.parse(JSON.stringify())

原理:用JSON.stringify将对象转成JSON字符串,再用JSON.parse()把字符串解析成对象,一去一来,新的对象产生了,而且对象会开辟新的栈,实现深拷贝。

  const obj1 = {
    user_info:{
      name: "Nancy",
      age: 18,
      gender: "女",
    },
    hobbies: ["eat", "sleep", "game"],
  }
    const obj2 = JSON.parse(JSON.stringify(obj1)); 
    obj2.user_info.name = 'Juliet'
    
    console.log(obj1,'obj1');
    console.log(obj2,'obj2')

在这里插入图片描述

注意点:
这种方法虽然简单方便,可以实现数组或者对象的深拷贝。但是不能处理函数正则,因为这两者基于JSON.stringify 和 JSON.parse处理后,得到的正则就不再是正则(变为空对象),得到的函数就不再是函数(变为null)了。

const obj1 = {
      name: "Nancy",
      age: 18,
      gender: "女",
      hobbies: ["eat", "sleep", "game"],
      //函数
      watchComic: () => {
        console.log("Nancy 你好");
      },
      //正则
      regx: /^icy{3}$/g,
    };
    const obj2 = JSON.parse(JSON.stringify(obj1)); 
    console.log(obj2,'obj2');

在这里插入图片描述

可看到上图打印,函数没了,正则变成空对象{}。
这是因为 JSON.stringify() 方法是将一个JavaScript值(对象或者数组)转换为一个 JSON字符串,不能接受函数。

2)手写递归方法

递归方法实现深度克隆原理:遍历对象、数组直到里边都是基本数据类型,然后再去复制,就是深度拷贝。

手动递归实现深拷贝,我们只需要完成以下2点即可:

  1. 对于基本数据类型,我们只需要简单地赋值即可(使用 “=” )
  2. 对于引用类型,我们需要创建新的对象,并通过遍历键来赋值对应的值,这个过程中如果遇到 Object 类型还需要再次进行遍历。(注意Array也属于Object类型)
export function deepCopy (obj1, obj2) {
  // 深拷贝
  if (obj2 === undefined) {
    if (Array.isArray(obj1)) {
      obj2 = []
    } else {
      obj2 = {}
    }
  }
  // 遍历目标数据
  for (let i in obj1) {
    if (obj1.hasOwnProperty(i)) {
      if (Array.isArray(obj1[i])) { //判断目标结构里的每一项值是否存在数组/对象
        obj2[i] = []
        deepCopy(obj1[i], obj2[i])
      } else if (typeof obj1[i] === 'function') {
        obj2[i] = obj1[i]
      } else if (obj1[i] instanceof Object) {
        obj2[i] = {}
        deepCopy(obj1[i], obj2[i])
      } else {
        obj2[i] = obj1[i]  // 这里的值就是基本数据类型了
      }
    }
  }
  return obj2
}
  const obj1 = {
      user_info: {
        name: 'Nancy',
        age: 18,
        gender: '女'
      },
      hobbies: ['eat', 'sleep', 'game'],
      // 函数
      watchComic: () => {
        console.log('Nancy 你好')
      }
    }
    
    let obj2 = deepCopy(obj1)
    obj2.user_info.age = 20
    obj2.user_info.gender = '男'
    
    console.log(obj1, 'obj1')
    console.log(obj2, 'obj2')

在这里插入图片描述

学习过程中也遇到一些很不错的文章:
彻底讲明白浅拷贝与深拷贝
前端深拷贝与浅拷贝(附实现方法)
谁动了我的数据 | 程序员必备小知识

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

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

相关文章

品牌投资与形象全面升级 | 快来认识全新的 Go 旅城通票

近日,Go 旅城通票(Go City)品牌全面升级,旨在提高旅游爱好者对品牌的认知。从新冠疫情大流行中阴霾中走出来的 Go 旅城通票复苏势头强劲,专注于技术提升,使命是协助旅游爱好者无论到世界各地的哪一个城市畅…

在线分析网站日志软件-免费分析网站蜘蛛的软件

搜索引擎蜘蛛的作用是什么?我们网站上的内容如果要想被搜索引擎收录并且给予排名,就必须要经过搜索引擎蜘蛛的爬取并且建立索引。所以让搜索引擎蜘蛛更好的了解我们的网站是很重要的一步!搜索引擎蜘蛛在爬取某个网站,是通过网站的…

浅谈虚拟地址转换成物理地址(值得收藏)

这里,我们讲解一下Linux是如何将虚拟地址转换成物理地址的 一、地址转换 在进程中,我们不直接对物理地址进行操作,CPU在运行时,指定的地址要经过MMU转换后才能访问到真正的物理内存。 地址转换的过程分为两部分,分段…

Linux systemctl 详解自定义 systemd unit

Linux systemctl 详解&自定义 systemd unit systemctl 序 大家都知道,我们安装了很多服务之后,使用 systemctl 来管理这些服务,比如开启、重启、关闭等等,所以 systemctl 是一个 systemd 系统。centos 使用 systemctl 来代…

9.8 段错误,虚拟内存,内存映射 CSAPP

相信写代码的或多或少都会遇到段错误,segmentation fault. 今天终于看到这里面的底层原理 参考: https://greenhathg.github.io/2022/05/18/CMU213-CSAPP-Virtual-Memory-Systems/18-Virtual-Memory-SystemsSimple memory system exampleAddress Trans…

(转)CSS结合伪类实现icon

老规矩,还是先说说业务场景:有一个图片列表,可以添加、删除和更改,其中呢删除时设计给的设计稿时悬浮(hover)在图片上时显示删除的图标,所以就有了这个用before实现icon的场景 进入正文&#xf…

嵌入式系统开发笔记108:IO的使用方法与面向对象程序设计

文章目录前言一、IO引脚的基本概念二、映射层的设置1、映射层是原理图的直译层2、IO引脚的设置在hal.h 和 hal.cpp文件中完成(1)在hal.h中进行类定义(2)在hal.cpp中完成引脚映射三、面向对象程序设计思想1、程序设计分类2、举例3、…

DevExpress之C#界面+MATLAB动态链接库联合编程

MATLAB导出动态链接库 在MATLAB命令行中输入:deploytool,打开如下界面,选择Library Compiler 对于C#,选择.NET Assembly,点击右侧的“+”加号,添加要导出的函数 可添加多个函数 下面的类名中输入即为导出后类的名称 点击设置按钮,输入参数-C,参数的具体含义如下 …

简化MRO工业品供采交易路径,S2B2B商城助力企业构建业务一体化管理优势

在政策拉动、市场需求驱动及数字技术进步等多重力量共同作用下,近5年来,我国工业品B2B市场规模保持上升的态势。尽管2022年受疫情反复影响,但中国经济向好的局面并未改变,中国数字化经济依然会加快工业品B2B市场的发展&#xff0c…

绿色债券数据集2016-2021(含交易代码、债券简称、发行规模期限等多指标数据)

1、数据来源:wind 2、时间跨度:2016.01-2021.11年 3、区域范围:全国 4、指标说明: 部分指标如下: 交易代码 债券简称 发行起始日 缴款日 计划发行规模(亿) 发行金额上…

第四章. Pandas进阶—时间序列

第四章. Pandas进阶 4.9 时间序列 1.重采样(resample) 在Pandas中,对时间序列频率的调整称为重采样,即时间序列从一个频率转换到另一个频率的过程,由周统计变成月统计 1).语法: 4.8章 第4点 已介绍过&…

5G无线技术基础自学系列 | MIMO功能

素材来源:《5G无线网络规划与优化》 一边学习一边整理内容,并与大家分享,侵权即删,谢谢支持! 附上汇总贴:5G无线技术基础自学系列 | 汇总_COCOgsta的博客-CSDN博客 无线通信的迅速发展对系统的容量和频谱…

天启星座(Tianqi)介绍

天启星座(Tianqi)由38颗卫星组网而成,提供全球短数据采集。致力于为全球物联网相关行业用户提供覆盖全球、准实时的物联网卫星数据服务,真正实现空中、海洋和地面的万物互联,构建天地一体化的卫星物联网生态系统&#…

stm32 笔记 UART读取及HAL库应用

基本流程图 由此图可知: 采用HAL库,中断方式接收串口,只有当RxXferCount 0 时,也就是调用这个函数,接收指定量的数据大小完成时,才会调用回调函数HAL_UART_RxCpltCallback()。 而且,RxXferCou…

技术资料:STM32F746NGH7,STM32L471ZGT6 IC MCU+FPU

描述:STM32F7 32 位 MCUFPU 基于高性能的 ARMCortex-M7 32 位 RISC 内核,工作频率高达 216MHz。Cortex-M7 内核具有单浮点单元(SFPU)精度,支持所有 ARM 单精度数据处理指令与数据类型。同时执行全套 DSP 指令和存储保护单元(MPU&a…

ThreadLocal源码解析 2.ThreadLocalMap内核

ThreadLocal源码解析—ThreadLocalMap内核 简介 内部类 ThreadLocalMap 才是 ThreadLocal 的真正核心。 ThreadLocalMap 与 HashMap不一样,HashMap 中的数据结构有数组,链表还有红黑树;而 ThreadLocalMap 中的数据结构只有数组。HashMap 处…

反转链表与拓展【灵神基础精讲】

来源0x3f:https://space.bilibili.com/206214 文章目录反转链表[206. 反转链表](https://leetcode.cn/problems/reverse-linked-list/)[92. 反转链表 II](https://leetcode.cn/problems/reverse-linked-list-ii/)[25. K 个一组翻转链表](https://leetcode.cn/proble…

[附源码]Python计算机毕业设计Django仓储综合管理系统

项目运行 环境配置: Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术: django python Vue 等等组成,B/S模式 pychram管理等等。 环境需要 1.运行环境:最好是python3.7.7,…

windows改linux

使用旧的windows电脑改成linux机器,不使用双系统,直接格式化 本次需要两个U盘或者两台电脑。 制作WINPE启动盘 使用U深度制作启动盘,制作完成后进入PE系统,然后使用diskGenius删除所有电脑的分区进行快速分区,格式选…

R语言主成分回归(PCR)、 多元线性回归特征降维分析光谱数据和汽车油耗、性能数据...

原文链接:http://tecdat.cn/?p24152什么是PCR?(PCR PCA MLR)(点击文末“阅读原文”获取完整代码数据)。• PCR是处理许多 x 变量的回归技术• 给定 Y 和 X 数据:• 在 X 矩阵上进行 PCA– 定…