在讲JavaScript(js)深度克隆之前,我们先来了解一下js中对象的组成。 js中,每一个实例都是一个对象,具体分为:
原始类型
和
配置类型
:
原始类型
对象是指按值传递的Unknown 、 Null 、 Boolean 、 Number 和String 。
配置类型
对象指的是数组、对象和函数。这些是通过地址传递的。当传递时,它将是内存中的一个地址。
有两种类型的克隆或复制。
浅克隆
,
深度克隆
。
浅克隆
:基本类型按值传递,但对象仍然按引用传递。一种复制方法,其中复制数据的更改会影响原始数据。
深度克隆
:每个元素或属性都是完全复制的,并且完全独立于原始引用类型。这意味着即使稍后对象的属性发生更改,原始对象也保持不变。修改复制的数据是一种不影响原始数据的复制方法。
JS中的深拷贝和浅拷贝一直是一个热门话题。简而言之,复制涉及使用某种方法来生成与复制的数据(几乎)完全相同的数据。 JS,拷贝有两种:深拷贝(深克隆)和浅拷贝。
一个普遍的理解是,无论如何修改复制得到的数据,都不应该影响原始数据。例如,我们在日常生活中使用复制和粘贴,但我从未见过有人修改复制的Word文档。结果,原来的文件也被修改了。这个应该是从js内存空间开始的。
JS内存空间补充:JS内存空间由常量池、栈、堆组成。堆用于存储引用类型数据,例如数组和对象。这些都是引用类型数据。对象在内存空间中的存储格式如下。对象名和引用存储在栈空间中,引用指向堆空间中的对象。同样的事情也适用于数组。
我们都知道,当通过赋值操作或其他方法生成复制对象时,它实际上是原始对象,因为它只是对该对象的引用。如果修改一个新对象,其内容就是该对象的内容。原始对象也被修改。由于原对象与新对象的引用相同,因此就出现了深拷贝和浅拷贝的问题。
浅克隆(Shallow Copy) 如果数据类型是引用类型,给这个变量赋值实际上是指这个变量在内存中的地址。如下:
1.
直接赋值
如果数据类型是引用类型,给这个变量赋值实际上是指这个变量在内存中的地址。如下:
var obj={name: \’ccc\’,age: 18} //定义变量为对象,引用类型var cloneObj=obj //创建新变量并赋值console.log(cloneObj) //{name: \’ccc\’,age: 18} console.log(cloneObj===obj) //true2.
对象.assgin():
const arr1=[1, 2, 3]const arr2=Object.assign(arr1)arr2[0]=5console.log(arr1) //[5, 2, 3]console.log(arr2) //[5, 2, 3]3. Array.prototype.concat()(浅拷贝)
let arr=[1,2,3,4,{username:\’kebi\’,age:18}]; let arr1=arr.concat() //concat 是连接数组。如果不传参数,则复制以下数据: 4.目标数组的Array.prototype.slice()(浅拷贝)
let arr2=arr.slice(); //如果不传递参数,切片默认为all。
浅克隆带来的问题:
复制的数据不能包含函数,也不能被处理。复制的数据是参考,复制数据的更改会影响原始数据。深度复制(深度克隆),复制过程中会产生新的数据。更改不影响原始数据
var obj={name: \’ccc\’,age: 18} //定义变量为对象,引用类型var cloneObj=obj //创建新变量并赋值console.log(cloneObj) //{name: \’ccc\’,age: 18} console.log(cloneObj===obj) //trueobj.name=\’www\’console.log(cloneObj) //更改了{ name: \’www\’,age: 18 } 的属性值即可看到。 obj变量和cloneObj的属性值也发生了变化。原因是虽然它们是两个变量,但它们引用的变量是同一个变量。参见下面的图表分析。
深度克隆(深度复制) 1.
确定要复制的对象的类型
2.
它根据类型产生一个空对象或空数组,并且可以直接返回其他基本数据类型。
3.
调用for in方法遍历复制的对象(数组),并向新对象(数组)添加数据。如果是基本数据类型,直接添加到新的数组(对象)中。否则,深度克隆数据。子进度
递归
就是这样。
深克隆解决了浅克隆带来的问题。直接上代码。
function deepClone(o) { //判断是否不是引用类型,直接返回数据if (typeof o===\’string\’ || typeof o===\’number\’ || typeof o===\’ | boolean\’ | typeof o===\’unknown\’) { return o } else if (Array.isArray(o)) { //对于数组,定义一个新数组,复制完成后返回。此处不能使用数组,因为typeof 数组返回对象。 console.log(typeof []) //– object var _arr=[] o.forEach(item={ _arr.push(item) }) return _arr } else if (typeof o===\’object\’) { var _o={} for (let key in o) { _o[key]=deepClone(o[key]) } return _o }}var arr=[1, 2, 3, 5] var cloneArr=deepClone(arr)console. log(cloneArr) //– [ 1, 2, 3, 5 ]console.log(arr===cloneArr) //– falsevar obj={ name: \’ ccc\’,age: 18 }var cloneObj=deepClone(obj) console.log(cloneObj) //– { name: \’ccc\’,age: 18 }console.log(obj===cloneObj) //falseobj.name=\’www \’console.log (obj) //– { name: \’www\’,age: 18 }console.log(cloneObj) //– { name: \’ccc\’,age: 18 }obj和cloneObj各自指向自己的变量地址,代码注释互不影响。参见下图:
注:上图中的深度克隆代码仅供参考。有许多细节没有考虑到,例如数组和对象的嵌套副本。具体用法参见Lodash的cloneDeep()方法。
Json.parse(Json.Stringfy()) (深拷贝)
let arr3=JSON.parse(JSON.stringify(arr));//先将原数组转换为json字符串,改为base数据类型,完全生成新数据,然后转换为原数组js数组,利用这个实现深复制
本文和图片来自网络,不代表火豚游戏立场,如若侵权请联系我们删除:https://www.huotun.com/game/651004.html