踩坑深浅拷贝

173 阅读3分钟

背景

某天测试提了个bug,问题是这样的:页面根据物品类型展示的汇总数据是正确的,但是非汇总数据(按照TO单)物料数据有误。哎呀,赶紧去看代码,由于这块是从其他人那边接手过来的,找到代码所在之后,看了下处理逻辑:从后台拿过来的数据是按照TO单来的,前台遍历了数据里的TO单物料字段(一个数组对象),按照物料类型进行了数据汇总。

问题原因

经过分析后,发现问题就出来汇总那里,汇总的数据是直接在某个类型的第一个物料基础上进行求和计算的,这就导致了汇总处理之后,TO单里的各种类型的第一个物料的数据变成了汇总后的数据,后面的其他物料数据不受影响。这个就是典型的JS引用类型直接赋值(赋值引用即浅拷贝)导致的问题了。

问题解决

由于数组里的对象不是复杂的对象,只是简单的一层结构,所以遍历数组时使用Object.assign方法实现物料对象的深拷贝,这样后续的处理就不会影响到原始数据了。

聊聊深浅拷贝

  • 什么是深浅拷贝 先说说值类型和引用类型: 值类型:数据存储在栈内存中,一个变量向另一个变量赋值,会在栈内存中创建一个副本,即拷贝实例(深拷贝),这种情况下,改变其中一个值,不会影响到另一个值。 引用类型:数据存储在堆内存中,引用类型变量存储的实际是一个指向数据对象的指针,一个变量向另一个变量赋值时,复制的是指针地址,即拷贝引用(浅拷贝),这样,两个变量最终指向的是同一个指针,当改变其中一个变量时,另一个变量自然也改变了。

  • 浅拷贝实现 数组、对象等引用类型的通过赋值操作符=可以实现浅拷贝

    var arr1 = [1,2,3];
    var arr2 = arr1;
    arr2[1] = 5;
    console.log(arr1);  //[ 1, 5, 3 ]
    

    可以看到通过修改arr2的值,arr1的值也跟着发生了变化

  • 深拷贝实现 先提供一个终极解决方案,lodash工具,lodash提供的cloneDeep方法,会递归拷贝传入的值,实现深拷贝,有兴趣的,可以看下lodash是怎么实现的。接下来看下各种引用类型在数据结构不是很复杂的情况下,不引用其他插件,如何实现深拷贝。

    1. 数组的深拷贝
    • 数组遍历赋值,如果存在多维数组就不通用了
    • 一些数组方法,适用简单数组
      Array.slice(0)
      Array.map(function(item){
           return item
      })
      Array.concat() 
    
    • ES6拓展运算符,适用简单数组,数组里元素是对象等就不生效了 var [...arr2] = arr;
    • JSON.stringify()、JSON.parse(),不考虑数组里有function、undefined等不能被文本化的类型; var arrDeepCopy = JSON.parse(JSON.stringify(arr));
    1. 对象的深拷贝
      • 简单粗暴:循环遍历赋值新对象
      • 对象的方法:Object.assign(),对象存在多层不适用
      • JSON.stringify()、JSON.parse()

持续更新中...