JS面试题-<变量和类型>-JavaScript浅拷贝与深拷贝

前言

  最开始了解到深浅拷贝是因为准备面试,但那个时候因为在学校做的项目比较少需求也比较简单,所以没有在项目中遇到这类问题,所以对这个问题就属于知道这个知识点,看过相关内容,却没有自己的总结,也没有深入的了解。后来在工作中遇到过两次这样的问题,第一次遇到后我写了一篇文章《在vue项目中遇到关于对象的深浅拷贝问题(地址指向)》https://www.cnblogs.com/songForU/p/11187861.html,记录了需求、错误代码及解决方案,那个时候认为自己的解决方案就是深拷贝;然而第二次遇到问题后,无论我怎么使用那些方案都无法解决问题,也写了一篇文章记录了一下解决方案,《javascript数组/对象数组的深浅拷贝问题》https://www.cnblogs.com/songForU/p/11469498.html,但是很遗憾我依旧没有搞明白之前我认为的“深拷贝”为什么出现了问题,可笑的是依旧没有去深入了解,只是简单的记住了解决方案。

  直到最近我组长给我一个截图,上面是关于深浅拷贝的知识点。我看了内容就懵了,为啥我之前认为“深拷贝”方法是浅拷贝,那到底什么是深浅拷贝?截图内容大致如下:

 

浅拷贝

  拷贝属性值到新的对象中,如果属性是对象的话拷贝的是地址,对象属性指向共同的地址;其中Object.assign()和...可以实现浅拷贝。

深拷贝

  拷贝属性值到新的对象中;两个对象没有任何关系;可以通过JSON.parse(JSON.stringify(object))进行大多数的拷贝,;这个方法有一定的局限性,如下:

  1、会忽略undefined

  2、会忽略symbol

  3、不能序列化函数

  4、不能解决循环引用的对象

一、浅拷贝

  问:什么是浅拷贝?

  涉及到深浅拷贝则是关于对象的复制,但是并不是所有的复制操作都叫做浅拷贝。现在我的理解是,我们对引用类型进行复制想要的结果无非是,两个变量互不影响即可,也就是所谓的深拷贝。当你在进行对象复制的时候,只要保证了复制后的对象和被复制的对象的内存地址是两个完全不同地址,则便达到了“深拷贝”,可能你这种方法并不适用于所有对象,有局限性罢了,但是确实是达到了效果。

  我之前对深浅拷贝产生误解就是由于自己认为的那些完成了“深拷贝”的方法不适用与所有对象罢了,特别是那些有嵌套对象关系的。

  1)Object.assign()、展开运算符...、Array.prototype.slice()、concat等真的不能达到“深拷贝”的效果么?

  凡事都有例外,想找到一个普世通用的理论或方法,总那么不尽人意,但总能解决一部分的问题。

 像下面这种没有嵌套关系的对象、数组,即第一层为基本数据类型的,使用上面的几个方法都可以达到“深拷贝”的效果,去改变复制后的数据不会使原数据同一改变,因为此时两者的内存地址也是不同的。

let obj = {
  name:"song",
  age:1
}

let arr = ["1","2","3"];

  但是对于复杂的数组,嵌套的对象则不适用,即第一层不是基本数据类型而是引用数据类型;使用上述方法后在改变数据包含的对象则会使原数据一同改变。

let obj = {
  name:"song",
  age:1,
  job:{
    price:10,
    work:"eng"
  }
}

let arr = ["1","2",[2,3]];
let arr = ["1","2",{name:"s",age:1}];            

  当然也不是说,只要是复杂嵌套的对象就一定不能使用上述方法来达到”深拷贝“的效果。如果在开发项目中,你知道数据的结构便可以对数据二次遍历操作,从而达到"深拷贝"的效果。就比如我之前做的项目中的使用方法。

dataList.map(o => ({...o}));

  2)深拷贝

所谓的深拷贝就是会拷贝所有的属性后,操作任意一个,两者互不影响,无论是几层嵌套都能够各自独立。

一般来说比较简单的方法是利用JSON.parse(JSON.stringify(object)),但该方法也存在一些问题,只不过比刚才浅拷贝的几种方法,解决拷贝问题的通用性更高一些罢了。 

当对象里面包含有以下内容,哪怕是一层结构也会有问题。

1、会忽略 undefined

2、会忽略 symbol

3、不能序列化函数

4、不能解决循环引用的对象

 

  示例如下: 

let obj = {
  name:"song",
  va:undefined,
  vb:Symbol('song'),
  vc:function () {}
}
console .log(obj);
let a = JSON.parse(JSON.stringify(obj));
console.log(a);

 

  3)怎么样实现一个深拷贝?

  核心的点是三个:

    (1)判断是值类型还是引用类型,不是引用类型则直接返回;

    (2)引用类型是数组还是对象

    (3)递归

 

 1 function deepClone(obj){
 2   //先判断obj是否是引用类型
 3   if(typeof obj !=='object'){
 4   return obj;      
 5 }
 6   //判断obj是数组还是对象,并初始化返回类型
 7     var objNew = Array.isArray(obj) ? [] :{};
 8    //如果obj存在且为引用类型,循环遍历属性key
 9   if(obj&& typeof obj === 'object'){
10     for(var key in obj){
11 12   if(obj.hasOwnProperty(key)){
13     //如果obj的子元素依旧是对象,那么进行递归操作,如果不是,则直接赋值
14     objNew[key] = (obj[key] && typeof obj[key] === 'object') ? deepCopy(obj[key]):obj[key];
15   }  
16 }
17 } 
18 return   objNew ;
19 }

 

posted @ 2019-11-14 14:32  居老师的狗子  阅读(889)  评论(0编辑  收藏  举报