0


JS最完整的深浅拷贝实现方式

深浅拷贝:

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

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

基本类型拷贝

  1. let a = 1;
  2. let b = a;
  3. b = 2;
  4. console.log(a);//1
  5. console.log(b);//2

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

数组拷贝

concat() slice()

  1. // concat()
  2. let list = ['a','b','c'];
  3. let list2 = list.concat();
  4. list2.push('d')
  5. console.log(list);//['a','b','c']
  6. console.log(list2);//['a','b','c','d']
  7. // slice()
  8. let list = ['a','b','c'];
  9. let list2 = list.slice();
  10. list2.push('d')
  11. console.log(list);//['a','b','c']
  12. console.log(list2);//['a','b','c','d']

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

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

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

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

对象拷贝

new Object()

  1. let a = {id:1,name:'a',obj:{id:999}};
  2. let b = new Object();
  3. b.id = a.id;
  4. b.name = a.name;
  5. b.obj = a.obj;
  6. a.name = 'b';
  7. a.obj.id = 888;
  8. console.log(a);
  9. console.log(b);

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

Object.assign

  1. let a = {id:1,name:'a',obj:{id:999}};
  2. function fun(obj){
  3. let o = {};
  4. Object.assign(o,obj);
  5. return o;
  6. }
  7. let a2 = fun(a);
  8. a2.name ='a2';
  9. a2.obj.id = 888;
  10. console.log(a);
  11. console.log(a2);

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

JSON.parse(JSON.stringify( ))

  1. let a = {
  2. name : 'a',
  3. age : 20,
  4. obj : {id:999},
  5. action : function(){
  6. console.log(this.name);
  7. }
  8. }
  9. let b = JSON.parse(JSON.stringify(a));
  10. a.name = 'b';
  11. a.obj.id = 888;
  12. console.log(a);
  13. console.log(b);

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

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

递归

  1. let a = {
  2. name:'a',
  3. skin:["red","blue","yellow",["123","456"]],
  4. child:{
  5. work:'none',
  6. obj:{
  7. id:999
  8. }
  9. },
  10. action:function(){
  11. console.log(this.name);
  12. }
  13. }
  14. //封装的递归方法
  15. function copyWid(obj){
  16. let newObj = Array.isArray(obj)?[]:{};
  17. for (var i in obj){
  18. if(typeof obj[i] === 'object'){ //判断是不是对象(数组或对象)
  19. newObj[i] = copyWid(obj[i]) //递归解决多层拷贝
  20. }else{
  21. newObj[i] = obj[i]
  22. }
  23. }
  24. return newObj;
  25. };
  26. let b = copyWid(a);
  27. b.child.obj.id =888;
  28. b.skin[3][0] = "pink";
  29. console.log(a);
  30. console.log(b);

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

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

展开运算符

  1. let a = {name:'a',id:99};//如果是数组[xx,xx,xx],{...a}需要改成[...a]
  2. let b ={...a}; //[...a]
  3. a.id =88;
  4. console.log(a);
  5. console.log(b);

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


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


本文转载自: https://blog.csdn.net/qq_20495901/article/details/123145742
版权归原作者 Beannnnnnn 所有, 如有侵权,请联系我们删除。

“JS最完整的深浅拷贝实现方式”的评论:

还没有评论