6

好久没写文章了,都感觉自己最近没什么进步啊,正愁不知道写什么呢,
这两天的工作中偶然发现一个js数组深拷贝的问题,正好可以拿出来说一说。

抛出问题

const arr = [{name: 'wens'},{age: '26'}];    //原数组  
const newArr1 = arr.slice(0);  
const newArr2 = arr.concat();  
newArr1[0].name = 'leon';  
newArr2[1].age = '27';  

在控制台打印之后发现,尼玛arr,newArr1,newArr2打印的东西完全一样完全一样完全一样

[{name: 'leon'},{age: '27'}];

说好的对数组进行slice或concat操作都是拿出一块新的内存空间存放新的数组,而不是对原数组的引用呢。难道之前我们对数组的深拷贝都被“骗了”。。。
别担心,下面,我们一步步来分析分析

从复制说起

抛开slice,concat方法不说,最原始复制一个数组的方法是什么

var arr = [1,2,3]
var newArr = arr

优点:方便
缺点:由于数组是引用类型(不懂基本类型和引用类型的自行补课),修改了arr或者newArr中的一个会影响全部

clipboard.png

既然大家都知道赋值的缺点,所以才有了slice,concat方法。
重点来了,搬好小板凳做好听课:

slice 不修改原数组,只会返回一个浅复制了原数组中的元素的一个新数组。原数组的元素会按照下述规则拷贝:
如果该元素是个对象引用 (不是实际的对象),slice 会拷贝这个对象引用到新的数组里。两个对象引用都引用了同一个对象。如果被引用的对象发生改变,则新的和原来的数组中的这个元素也会发生改变。
对于字符串、数字及布尔值来说(不是 String、Number 或者 Boolean 对象),slice 会拷贝这些值到新的数组里。在别的数组里修改这些字符串或数字或是布尔值,将不会影响另一个数组。
如果向两个数组任一中添加了新元素,则另一个不会受到影响。

引用MDN中的话,翻译成白话:
数组中的值如果是引用类型,对其进行增删改,会影响用slice复制的数组,
但是如果数组中的值是基本类型,就不会影响
现在回头看开篇提出的那个问题,就很好理解了,因为数组里面是obj,而obj是引用类型,所以当然修改了newArr,arr里面的值也修改了
其实,工作中遇到的很多奇奇怪怪的问题大部分都是对知识点的模棱两可,仔细看文档就能解决的问题,所以说学好技术有时候并没有大家想的那么简单,仔细仔细仔细,认真认真认真能做到这两点的人好少啊

解决方案

1、简单粗暴:

newArr1 = JSON.parse(JSON.stringify(arr));

这种方式能够满足99%的深拷贝(如开篇的例子),但是对与RegExp类型和Function类型则无法完全满足,而且不支持有循环引用的对象。

2、现成的lodash库:_.cloneDeep


wens
272 声望4 粉丝