快速理解JS深浅拷贝即使用

快速JS理解深浅拷贝

自我记录

拷贝通俗的讲就是复制 Ps:本文只是个人见解以及概括,如有不同观点可在评论区指出共同学习!
首先想理解深拷贝浅拷贝、个人认为需要知道4个知识点 JS基本数据类型引用类型
想直接使用的话可以直接看 4.14.24.3(深拷贝)之所以墨迹这么多是觉得我们不仅应该会用也要知道些原理
4.1... ES6展开运算符 (浅)
4.2Object.assign(target,sources) ES6方法 (浅)
4.3JSON.parse(JSON.stringify(对象))序列化反序列化法 (深)

一. JS数据类型

1.1 基本数据类型(7个)

1.Number(数值,包含NaN)
2.String(字符串)
3.Boolean(布尔值)
4.Undefined(未定义/未初始化)
5.Null(空对象)
6.Symbol(独一无二的值,ES6 新增)
7.BigInt (任意精度格式的整数,能够表示超过 Number 类型大小限制的整数,ES10新增)

1.2 引用类型

1.Object(包含Array、Function等)

1.3 基本数据类型与引用类型区别

(1).基本数据类型(存放在中)

基本数据类型是指存放在中的简单数据段,数据大小确定,内存空间大小可以分配,它们是直接按存放的,所以可以直接按访问

(2).引用类型 (存放在中)

引用数据类型在中存储了指针(一个地址),该指针指向堆中该实体的起始地址。当解释器寻找引用值时,会首先检索其在中的地址,取得地址后从堆中获得实体。

二. 内存中的栈和堆

2.1 什么是栈

先进后出;由操作系统自动分配释放,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。

2.2 什么是堆

队列优先,先进先出;动态分配的空间 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收,分配方式类似于链表。

2.3 区别概括

个人从本文角度理解:基本数据类型在栈里面存的是值引用类型在栈里面存的是一个指针(感觉就是一个地址)用于指向对应堆里面的这个地址 由此牵扯出浅拷贝和深拷贝。

具体详情概述请参考网上其他文章

三. 浅拷贝与深拷贝

1.赋值基本数据类型就是把栈中的值复制给出去了,但是赋值引用类型给出去的是栈中对应堆的的地址, 而不是堆中的数据。也就是两个对象指向的是同一个存储空间,无论哪个对象发生改变,其实都是改变的存储空间的内容,因此,两个对象是联动的,一旦一方修改,另一方也会受到影响。
2.因此:基本数据类型没有浅/深拷贝一说

3.1 浅拷贝

前面已经提到,在定义一个对象或数组时,变量存放的只是一个地址。当我们使用对象拷贝时,如果属性是对象数组时,这时候我们传递的也只是一个地址。因此子对象在访问该属性时,会根据地址回溯到父对象指向的堆内存中,即父子对象发生了关联,两者的属性值会指向同一内存空间联动
简单理解:浅拷贝就是只拷贝一层数据
常见的方法有: ...Object.assign(target,sources)

3.2 深拷贝

会另外创造一个一模一样的对象,新对象跟原对象不共享内存,修改新对象不会改到原对象。

四. 分享常用拷贝的三个方法

4.1 ES6 展开运算符(浅拷贝)

没有进行浅拷贝如下:

  // 没有进行深拷贝
  let a = [1, 2, 3]
  let b = a
  a[0] = 100
  console.log(a) // [100, 2, 3]
  console.log(b) // [100, 2, 3]

使用...浅拷贝如下: 针对只有一层数据

  // 进行浅拷贝
  let a = [1, 2, 3]
  let b = [...a]
  a[0] = 100
  console.log(a) // [100, 2, 3]
  console.log(b) // [1, 2, 3]

多层数据结构例如二维数组形式 此方法只能改第一层

  // 只拷贝了一层数据
  let a = [1, 2, 3, [40, 50]]
  let b = [...a]
  a[0] = 10
  a[3][0] = 400
  console.log(a)  // [10, 2, 3, [400, 50]]
  console.log(b)  // [1, 2, 3, [400, 50]]

4.2 ES6 Object.assign(target,sources)(浅拷贝)

没有进行浅拷贝如下:

  // 没有进行深拷贝
  let a = [1, 2, 3]
  let b = a
  a[0] = 100
  console.log(a) // [100, 2, 3]
  console.log(b) // [100, 2, 3]

使用Object.assign浅拷贝如下: 针对只有一层数据

  // 进行浅拷贝
  let a = [1, 2, 3]
  let b = Object.assign([], a)
  a[0] = 100
  console.log(a) // [100, 2, 3]
  console.log(b) // [1, 2, 3]

多层数据结构例如二维数组形式 此方法只能改第一层

  // 只拷贝了一层数据
  let a = [1, 2, 3, [40, 50]]
  let b = Object.assign([], a)
  a[0] = 10
  a[3][0] = 400
  console.log(a) // [10, 2, 3, [400, 50]]
  console.log(b) // [1, 2, 3, [400, 50]]


在这里插入图片描述

要想不发生变化 那么就不能共用xx002 也可以理解为 b的堆里面用的是 xx003 和 xx004
在这里插入图片描述
显然 展开运算符 无法实现

4.3 序列化反序列化法 (深拷贝)

JSON.parse(JSON.stringify(对象))

  let a = [1, 2, 3, [40, 50]]
  let b = JSON.parse(JSON.stringify(a))
  a[0] = 10
  a[3][0] = 400
  console.log(a, 'a') // [10, 2, 3, [400, 50]]
  console.log(b, 'b') // [1, 2, 3, [40, 50]]

4.4 递归(深拷贝)

只考虑数据层面

 // 递归
 function fnDeepCopy(newObj, obj) {
    
    
     // 遍历对象
     for (key in obj) {
    
    
         // newObj[key] = obj[key] 拷贝一层
         // 如果obj[key] 是数组,又要重新遍历数组拷贝,在如果obj[key]是对象也要重新遍历拷贝,否则就直接拷贝一层
         if (obj[key] instanceof Array) {
    
    
             // 先判断数组,因为Array也是对象的一种
             newObj[key] = []
             fnDeepCopy(newObj[key], obj[key])
         } else if (obj[key] instanceof Object) {
    
    
             newObj[key] = {
    
    }
             fnDeepCopy(newObj[key], obj[key])
         } else {
    
    
             newObj[key] = obj[key]
         }
     }
 }

 let a = [1, 2, 3, [40, 50]]
 let b = []
 fnDeepCopy(b, a)
 a[0] = 10
 a[3][0] = 400
 console.log(a) // [10, 2, 3, [400, 50]]
 console.log(b) // [1, 2, 3, [40, 50]]

在这里插入图片描述
总结不易,希望对各位有所帮助.希望可以共同学习与进步.(赞与收藏就是我的动力)
以上就是我常用的两种方式.
顺便分享一位大佬的:深入 js 深拷贝对象:https://www.jianshu.com/p/b08bc61714c7

猜你喜欢

转载自blog.csdn.net/zhgweb/article/details/130807952