快速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