JS数组的深浅拷贝

时间:2021-10-18 19:47:25

javascript数组在使用时,时常会遇到数组备份的情况,之后对数组做些修改,再同原数组进行比对,查看数组的变化,这里就涉及到一个数组拷贝的问题。

浅拷贝只复制一层对象的属性;深拷贝递归复制了所有层级。


数组的拷贝,通常可以使用一个新的数组,指向现有数组

var arr = [el1, el2, el3...];
var arr2 = arr;

这种写法,待arr2做改变时,我们查看arr会同步做修改

ex 

var arr = ['liuche', 'zhouyafu', 'huoqubing', 'weiqing'];
var arr2 = arr;
arr2.push('liguang');
alert(arr); // 'liuche', 'zhouyafu', 'huoqubing', 'weiqing', 'liguang'
alert(arr2); // 'liuche', 'zhouyafu', 'huoqubing', 'weiqing', 'liguang'

这里我们看到,待修改数组arr2时,arr同时做了改变,这显然不是我们想要的结果。

示例中,这种直接将数组引用复制的方式就是浅拷贝。


那我们想要对数组进行备份的话,该如何操作呢,可以借助于Array对象的slice()方法和concat()方法。

slice方法

slice() 方法可从已有的数组中返回选定的元素。

arrayObject.slice(start,end)

其中:

start,必需。规定从何处开始选取。如果是负数,那么它规定从数组尾部开始算起的位置,从0开始。也就是说,-1 指最后一个元素,-2 指倒数第二个元素,以此类推。

end,可选。规定从何处结束选取。该参数是数组片断结束处的数组下标。如果没有指定该参数,那么切分的数组包含从 start 到数组结束的所有元素。如果这个参数是负数,那么它规定的是从数组尾部开始算起的元素。

slice方法返回一个新的数组,包含从 start 到 end (不包括该元素)的 arrayObject 中的元素。

若start为0,end 缺省,则相当于截取了整个数组的元素值,即我们这里要说的数组拷贝。

ex2

var arr = ['liuche', 'zhouyafu', 'huoqubing', 'weiqing'];
var arr2 = arr.slice(0);
arr2.push('liguang');
alert(arr); // 'liuche', 'zhouyafu', 'huoqubing', 'weiqing'
alert(arr2); // 'liuche', 'zhouyafu', 'huoqubing', 'weiqing', 'liguang'
但是,如果遇到多维数组,slice方法并不奏效

ex3

var arr = [['liuche', 'zhouyafu', 'weiqing'], ['chengajiao', 'weizifu', 'liupiao']];
var arr2 = arr.slice(0);
arr2.push('liguang');
alert(arr); // liuche,zhouyafu,weiqing,chengajiao,weizifu,liupiao
alert(arr2); // liuche,zhouyafu,weiqing,chengajiao,weizifu,liupiao,liguang

ex4

var arr = [['liuche', 'zhouyafu', 'weiqing'], ['chengajiao', 'weizifu', 'liupiao']];
var arr2 = arr.slice(0);
arr2[0][3] = 'liguang';
alert(arr); // liuche,zhouyafu,weiqing,liguang,chengajiao,weizifu,liupiao
alert(arr2); // liuche,zhouyafu,weiqing,liguang,chengajiao,weizifu,liupiao

上面两个示例中,ex3中,arr2修改后,arr并未跟着一起修改,因为arr2的修改,是给arr2新增了arr[3]元素;但是ex4中,arr2修改后,arr跟着一起修改了,原因在于arr中arr[0] 元素是个数组对象,并非单的数值。

concat方法

concat() 方法用于连接两个或多个数组。

该方法不会改变现有的数组,而仅仅会返回被连接数组的一个副本。

arrayObject.concat(arrayX,arrayX,......,arrayX)

其中

arrayX,必需。该参数可以是具体的值,也可以是数组对象。可以是任意多个。

concat方法返回一个新的数组。该数组是通过把所有 arrayX 参数添加到 arrayObject 中生成的。如果要进行 concat() 操作的参数是数组,那么添加的是数组中的元素,而不是数组。

使用concat() 方法时,若arrayX为空,则相当于原数组与一个空数组拼接,即返回原数组,做到原数组的拷贝。

那使用concat方法再来看看刚才的示例

ex5

var arr = [['liuche', 'zhouyafu', 'weiqing'], ['chengajiao', 'weizifu', 'liupiao']];
var arr2 = arr.concat();
arr2[0][3] = 'liguang';
alert(arr); // liuche,zhouyafu,weiqing,liguang,chengajiao,weizifu,liupiao
alert(arr2); // liuche,zhouyafu,weiqing,liguang,chengajiao,weizifu,liupiao
结果同ex4相同,并未得到我们想要的结果

实在无解之际,咨询了下一个专职做前端的同事,了解到一个万能的JS拷贝方法,无论是数组还是对象均可以实现深拷贝

JSON.parse(JSON.stringify(arr));
这个方法其实比较简单,先把所有的对象属性解析为简单数值,再将数值拼接解析为JS对象。

看之前的例子

ex6

var arr = [['liuche', 'zhouyafu', 'weiqing'], ['chengajiao', 'weizifu', 'liupiao']];
var arr2 = JSON.parse(JSON.stringify(arr));
arr2[0][3] = 'liguang';
alert(arr); // liuche,zhouyafu,weiqing,chengajiao,weizifu,liupiao
alert(arr2); // liuche,zhouyafu,weiqing,liguang,chengajiao,weizifu,liupiao

解决上面的问题。


经过上面的例子和分析,可以看出来,简单数组的拷贝可以通过slice方法和concat方法来实现,对于多维数组的实现,必须通过JSON.parse(JSON.stringify(obj))方法来实现。初次学习,有不当地方还望大家批评斧正。谢谢。