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

本文介绍了JavaScript中的深拷贝和浅拷贝概念,强调理解基本数据类型与引用类型的差异。文中列举了四种拷贝方法,包括ES6的展开运算符、Object.assign、JSON.parse与JSON.stringify以及递归实现的深拷贝,并通过示例代码展示了它们在处理多层数据结构时的行为差异。
178

被折叠的 条评论
为什么被折叠?



