JavaScript最完整的深浅拷贝实现方式详解

Calandra ·
更新时间:2024-09-20
· 986 次阅读

目录

基本类型拷贝

数组拷贝 

concat() slice()

对象拷贝

new Object()

Object.assign

JSON.parse(JSON.stringify( ))

递归

展开运算符

总结

深浅拷贝:

内存中一共分为栈内存和堆内存两大区域,所谓深浅拷贝主要是对js引用类型数据进行拷贝一份,浅拷贝就是引用类型数据相互赋值之后,例obj1=obj2;如果后面的操作中修改obj1或者obj2,这个时候数据是会进行相应的变化的,因为在内存中引用类型数据是存储在堆内存中,堆内存中存放的是引用类型的值,同时会有一个指针地址指向栈内存,两个引用类型数据地址一样,如果其中一个发生变化另外一个都会有影响;而深拷贝则不会,深拷贝是会在堆内存中重新开辟一块空间进行存放;

简单来说就是B复制了A,如果A发生了改变,如果B随之变化,那么是浅拷贝,如果B并没有发生变化,则是深拷贝。

基本类型拷贝 let a = 1; let b = a; b = 2; console.log(a);//1 console.log(b);//2

a,b都是属于基本类型,基本类型的复制是不会影响对方的,因为基本类型是每一次创建变量都会在栈内存中开辟一块内存,用来存放值,所以对基本类型进行拷贝是不会对另外一个变量有影响的,属于深拷贝。

数组拷贝  concat() slice() // concat() let list = ['a','b','c']; let list2 = list.concat(); list2.push('d') console.log(list);//['a','b','c'] console.log(list2);//['a','b','c','d'] // slice() let list = ['a','b','c']; let list2 = list.slice(); list2.push('d') console.log(list);//['a','b','c'] console.log(list2);//['a','b','c','d']

上面两种方法只能实现数组类型里面的单层深拷贝,如果是多层无法实现深拷贝。

//二维数组 let list = ['a','b','c',['d','e','f']]; let list2 = list.concat(); list2[3][0] = 'a'; console.log(list); console.log(list2);

可以看到原二维数组的值也发生了变化,说明没有实现深拷贝。

由此总结,concat和slice只能实现一维数组的深拷贝,不能对多维数组进行深拷贝,所以并不是一个完美的深拷贝方式。

对象拷贝 new Object() let a = {id:1,name:'a',obj:{id:999}}; let b = new Object(); b.id = a.id; b.name = a.name; b.obj = a.obj; a.name = 'b'; a.obj.id = 888; console.log(a); console.log(b);

a.name更改并没有影响到b.name,好像可以看作深拷贝,但第二层obj里面里面的id随之更改了,因此其实并不是深拷贝。

Object.assign let a = {id:1,name:'a',obj:{id:999}}; function fun(obj){ let o = {}; Object.assign(o,obj); return o; } let a2 = fun(a); a2.name ='a2'; a2.obj.id = 888; console.log(a); console.log(a2);

以上两种方法,对于一层对象都能实现深拷贝,但对于多层对象则无法实现,因此也不是一种完美的深拷贝方式。

JSON.parse(JSON.stringify( )) let a = { name : 'a', age : 20, obj : {id:999}, action : function(){ console.log(this.name); } } let b = JSON.parse(JSON.stringify(a)); a.name = 'b'; a.obj.id = 888; console.log(a); console.log(b);

单层对象name和多层对象obj.id的改变都没影响到原对象,因此是一个比较完美的深拷贝,但是能看到function好像并没有被拷贝。

可以看出这个方法对于一层和多层都能实现深拷贝,但是这个方法的缺陷是不能拷贝Function,所以在使用时,一定要注意数据类型。

递归 let a = { name:'a', skin:["red","blue","yellow",["123","456"]], child:{ work:'none', obj:{ id:999 } }, action:function(){ console.log(this.name); } } //封装的递归方法 function copyWid(obj){ let newObj = Array.isArray(obj)?[]:{}; for (var i in obj){ if(typeof obj[i] === 'object'){ //判断是不是对象(数组或对象) newObj[i] = copyWid(obj[i]) //递归解决多层拷贝 }else{ newObj[i] = obj[i] } } return newObj; }; let b = copyWid(a); b.child.obj.id =888; b.skin[3][0] = "pink"; console.log(a); console.log(b);

可以看到,无论是多层的对象还是多层的数组,都能实现深拷贝,而且能拷贝函数,这个是目前来说最完美的深拷贝方法。

通过这个递归能实现一个比较完美的深拷贝,能弥补上述提到的所有方法中的缺点。

展开运算符 let a = {name:'a',id:99};//如果是数组[xx,xx,xx],{...a}需要改成[...a] let b ={...a}; //[...a] a.id =88; console.log(a); console.log(b);

这个方法能实现对象数组单层时的深拷贝,但是多层无法实现深拷贝。

以上这么多方法,最优解应该属于JSON和递归的方法(递归解决了JSON无法拷贝函数方法的问题),但是根据需求数据类型的不同,可以选择更简单的方式。

总结

本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注软件开发网的更多内容! 



浅拷贝 拷贝 JavaScript

需要 登录 后方可回复, 如果你还没有账号请 注册新账号