JavaScript实现多层深拷贝的简单方法
作者:景籬
这篇文章主要为大家详细介绍了JavaScript中实现多层深拷贝的几个简单方法,文中的示例代码简洁易懂,感兴趣的小伙伴可以跟随小编一起学习一下
1.使用Json转换的方式
let a = [1,2,3,[4,5]] let b = Json.parse(Json.stringify(a))
缺点:
- 如果嵌套的是函数类型就不能用,会被忽略
- undefined不能用,会被忽略
- Symbol不能用,会被忽略
- Date对象会变成字符串
- 无法处理循环引用(会报错)
2.如果嵌套的有函数类型
let obj = { name:"Tom", age:18, fn(){ console.log("age:",this.age) }, tt:{project:"kkk"}, aa:[1,2,3] } let obj2 = {...obj} // 浅拷贝:只复制第一层属性值(如字符串、数字等),对于嵌套的对象或数组,复制的是引用地址 obj.fn = function(){ console.log("++age:",++this.age)} obj.fn() obj2.fn() obj.tt.project = "yyy" console.log("obj",obj.tt.project) console.log("obj2",obj2.tt.project) obj.aa[0] = 5 console.log("obj",obj.aa) console.log("obj2",obj2.aa)
注意到当修改函数的值时,数据不会产生相互干扰,但是当修改数组或者对象的值会有干扰,因为数组或者对象复制过去的一层都是地址,函数是因为我们修改的方式相当于重写一个函数,因此你新的函数会在内存中开辟一个新的存储空间。
3.使用递归实现深拷贝
实现思路:
- 边界处理:如果是null或者非object类型,直接返回
- 处理循环引用:存储已拷贝过的对象,避免死循环
- 创建新容器:判断是数组还是对象,创建相应的空容器[]或者{}
- 递归拷贝:遍历原始对象的属性,递归调用deepClone复制给新容器
let a = [1,2,3] console.log(typeof a) //object let b=null console.log(typeof b)//object let c=undefined console.log(typeof c)//undefined /** * 深拷贝函数:递归实现 * @param {any} oldData 需要拷贝的数据,可以是对象、数组或基本类型 * @return {any} 返回拷贝后的新数据,与原数据无引用关系 */ function deepClone(oldData) { // 判断当前处理的数据是否为对象类型,并且不是 null(因为 typeof null === 'object') if (typeof oldData === 'object' && oldData != null) { // 根据原始数据是数组还是对象创建一个新的空数组或空对象作为结果容器 let res = Array.isArray(oldData) ? [] : {}; // 遍历原始对象的所有自有属性(不包括继承来的属性) for (let k in oldData) { if (oldData.hasOwnProperty(k)) { // 对当前属性值递归调用 deepClone 函数进行深拷贝 // 并将返回的结果赋值给结果对象/数组对应的键 res[k] = deepClone(oldData[k]); } } // 返回已经完成深拷贝的对象/数组 return res; } else { // 如果当前数据是基本类型(如 number, string, boolean)或者函数等,则直接返回原值 // 因为基本类型在赋值时是值传递,不会产生引用问题 return oldData; } }
4.structuredClone()
const original = {a:1, b: {c:2}, d:new Date()}; const deepCopy = structuredClone(original) deepCopy.b.c = 20; console.log(original.b.c) //2 console.log(original.d.getTime() === deepCopy.d.getTime()) //true (Date被正确克隆了)
优点:
- 内置的全局函数,无需引库
- 专为深拷贝设计,性能好
- 支持循环引用
- 支持多种复杂类型,如Date、Map、Set、正则等
缺点:
- 无法拷贝函数
- 无法拷贝Error对象/DOM节点
5.知识延展
javascript 深拷贝 (多层嵌套解决方案)
数组的几种方法深拷贝总结
var arr = [3,4,1,6,4]//准备好一个数组 var arr2 = arr.concat()//concat合并方法 var arr4 = JSON.parse(JSON.stringify(arr))//用JSON方法转换 var arr5 = arr.slice(0)//slice拷贝方法 var [...arr6] =arr;//扩展运算符方法 var arr7 = copyArr(arr)//万能的for循环 function copyArr(arr){ let res = [] for(let i=0;i<arr.length;i++){ res.push(arr[i]) } return res } //冒泡排序 for(var j = 0;j<arr.length-1;j++){//// 遍历数组,arr.length - 1表示要遍历的次数 for(var i = 0;i<arr.length-1-j;i++){ // i<arr.length-1-j 是因为当第一次,找到最大数,放到最后,那么下一次,遍历的时候就不用吧最后一位算上,因为他已经是最大的 var done = true;//设置一个标志位,减少不必要的循环 if(arr[i]>arr[i+1]){//当前数字大于后一位数组 位置调换 var temp = arr[i] arr[i] =arr[i+1] arr[i+1] = temp done = false } } if(done){//调换位置后跳出本次循环 break;//减少不必要的排序,提高性能。 } } //冒泡排序思想:每一次对比相邻两个数据的大小,小的排在前面,如果前面的数据比后面的大就交换这两个数的位置 console.log('经过排序的原数组:'+arr) console.log('concat方法:'+arr2) console.log('转换为json再转为对象:'+arr4) console.log('slice方法:'+arr5) console.log('ES6扩展运算符方法:'+arr6) console.log('万能的for方法:'+arr7)
上面实现的几种方式写在了一起 搞不明白方法的可以看下我前几天发的数组方法帖子。
对象的深拷贝
对象的深拷贝相比数组也没有困难许多,列举三种个方法。
1.万能的for循环
var obj = { name: 'wayne', sex: 'man', old: '23' } var obj2 = copyObj(obj) function copyObj(obj) { let res = {} for (var key in obj) { res[key] = obj[key] } return res }
2.JSON方法
var obj = { name: 'wayne', sex: 'man', old: '23' } var obj2 = JSON.parse(JSON.stringify(obj)) //原理没什么好解释的,实在是够简单粗暴
JSON.stringify()方法将对象转化为字符串,但只会处理简单属性和简单属性数组,constructor属性丢失了
3.ES6扩展运算符
var obj = { name: 'wayne', sex: 'man', old: '23' } var { ...obj2 } = obj
对象的深拷贝推荐使用es6提供的扩展运算符的方法
到此这篇关于JavaScript实现多层深拷贝的简单方法的文章就介绍到这了,更多相关JavaScript深拷贝内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!