js如何正确复制一个数组

在写js代码的过程中,对数组的操作是必不可少的。但是由于数组是引用类型,所以在很多新手操作数组的过程中会或多多少的踩一些坑,这里介绍几个简单的复制一个数组的小方法。

先看代码:

1
2
3
4
5
var list = [1,2,3,4,5,6];
var list2 = list;
list2.push(1);
console.log(list); //=>1,2,3,4,5,6,1
console.log(list2); //=>1,2,3,4,5,6,1

这是初学者最容易犯的错误之一了。我有一个list,现在想用list2来复制这个list,然后对list2进行一些操作,但最后操作的结果却影响了list,这个当然不是我们想要的结果。这样看来,数组的赋值操作看起来是引用的传递,因为list和list2似乎都是指向同一个地址,这样当你操作两者中的任意一个时,都会影响到另外的一个。

那么我们再看下一个例子:

1
2
3
4
5
var list = [1,2,3,4,5,6];
var list2 = list;
list2 = [1,2,3];
console.log(list); //=>1,2,3,4,5,6
console.log(list2); //=>1,2,3

是不是惊奇的发现,list2和list为啥不同了,而且就算你再对list或者list2进行任何数组操作(push,pop等),都已经不会影响到另外的数组了。那这么看来,js的数组赋值似乎又成了值传递了。

首先我们要明确一点,js里面的数组是引用类型不会跑,在这样的情况下我们来说明一下两个例子的不同。

var list = [1,2,3,4,5,6] 是给list开辟了一个内存空间,这个内存空间内放着数组[1,2,3,4,5,6],然后我们用list2=list,这个时候,系统内并没有新的内存被开辟,他们两个公用了一个引用,也就是使用了同样的一个内存空间。所以此时对list进行的任何操作,对list2也会造成相同的影响。而在第二个例子中,虽然也有list2 = list,但是之后的list2 = [1,2,3]却为list2开辟了新的内存空间,也就是从这个时候开始,list2所指向的内存地址和list所指的地址已经不同了。那么既然两者的地址不同,那么对其中之一的操作自然也就不会影响到另外一个了。

ok,了解完数组的一些特性,我们就可以开始我们的正题了,怎么去正确的复制一个素组,这里给出三个简单的方法,具体使用哪个自己去选择。

1.遍历

最底层的方法,我们自己动手,来创造一个新数组。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

function copyList(list) {
if (list.length == 0) {
return;
}
var _tempList = [];
for (var i = 0; i < list.length; i++) {
_tempList[i] = list[i];
}
return _tempList;
}

var list = [1,2,3,4,5,6];

var list2 = copyList(list);

console.log(list);
console.log(list2);

list2.push(1);

console.log(list);
console.log(list2);

这里我们使用了自己创建的copyList方法来对整个数组进行遍历赋值产生新的数组。这样新产生的数组既成功复制了原数组,又成功的开辟新内存空间,不会出现前面的不愉快现象。但是有人觉得,自己封装函数实在是太麻烦了,有没有什么js底层自带的方法能能实现这一需求呢。当然有,看下面:

2.concat方法

concat方法是js中对两个数组进行连接的方法,具体使用的方法就是list.concat(list2)这样返回的数组就是这两个数组的链接。详细的说明可以去百度。

1
2
3
4
5
6
7
8
9
10
11

var list = [1,2,3,4,5,6];
var list2 = [].concat(list);

console.log(list);//1,2,3,4,5,6
console.log(list2);//1,2,3,4,5,6

list.push(1);

console.log(list);//1,2,3,4,5,6,1
console.log(list2);//1,2,3,4,5,6

concat所返回的是一个新的数组,显然是有新的内存空间,这样就不会与之前的list数组对象占用同一个引用,由引用所引发的问题自然就解决了。

3.slice方法

1
2
3
4
5
6
7
8
9
10
11

var list = [1,2,3,4,5,6];
var list2 = list.slice(0);

console.log(list);//1,2,3,4,5,6
console.log(list2);//1,2,3,4,5,6

list.push(1);

console.log(list);//1,2,3,4,5,6,1
console.log(list2);//1,2,3,4,5,6

同样的,利用返回新数组的方法来进行复制。

好了,关于js复制数组复制的方法还有很多,原理也是大同小异,无非是开辟新的空间来储存原数组的数据,这里只是列举其中几个,主要是我们需要理解引用类型和值类型之间的差异,这样才能真正规避操作引用所带来的无法预料的错误。