Technicalarticles

关于JavaScript中数组的浅拷贝和深拷贝实现方法
作者:赵刘伟 时间:2020-05-09 浏览量:

一般情况下,使用 “=” 可以实现赋值。但对于数组、对象、函数等这些引用类型的数据,这个符号就不好使了。本文讲解利用js原生已实现的方法,我们就可以不用自己写循环实现数组的拷贝复制。

原数组:

let array1 = [1,'a',true,null,undefined];

slice()方法

let c1 = array1.slice();

concat()方法

let cc1 = array1.concat();

from()方法

let fc1 = Array.from(array1);

push()方法

let pc1 = [];Array.prototype.push.apply(pc1,array1);

map()方法

let mc1 = array1.map(function(item){    return item;
    });

以上几种方法都能实现数组的浅拷贝,即数组的每一项只能是原始类型的数据,如果数组的项包含引用类型,如数组(即js中的二维数组),对象等,以上方法复制的项只是引用。
还有一种方法是,使用JSON进行转换,先将数组序列化为json字符串,然后再将字符串转换成json对象即可。

JSON转换

//code from http://caibaojian.com/javascript-array-copy.htmllet jsonc = JSON.parse(JSON.stringify(array1));

这种方法可以变相的实现深拷贝,但是这种方法也有其限制:

  • 首先,数组中的项如果是undefined,那么转换后将变为null

  • 如果数组的项为对象,那么对象之间不可相互引用。会造成循环引用,无法JSON序列化。

性能分析

以上几种方法都可以实现数组的拷贝,那么,每种方法的性能如何呢,我使用console.time()和console.timeEnd()跟踪当数组大小为100-10000000时,每个方法所用的时间。注意,这里数组每一项都是随机的5种原始值的一个,不包含引用类型。

数组大小forofsliceconcatfrompushmapjson
1000.593ms0.038ms0.034ms0.404ms0.054ms0.193ms0.042ms
10001.606ms0.052ms0.044ms1.311ms0.090ms0.897ms0.124ms
100002.272ms0.097ms0.093ms3.294ms0.145ms1.845ms0.772ms
10000014.665ms0.901ms0.730ms22.283ms4.002ms15.894ms9.101ms
1000000175.663ms16.051ms8.400ms235.900msMaximum call stack size exceeded144.058ms97.946ms
100000001597.242ms83.860ms124.781ms2425.540msMaximum call stack size exceeded1424.344ms1043.772ms

当数组大小超过1000000时,push方式就挂了,报错:Maximum call stack size exceeded

webkit浏览器使用cocat().非webkit使用slice()。·

补上之前文章的另外另种实现:

简单的引用复制

function shallowClone(copyObj) {
  var obj = {};
  for ( var i in copyObj) {
    obj[i] = copyObj[i];
  }
  return obj;
}
var x = {
  a: 1,
  b: { f: { g: 1 } },
  c: [ 1, 2, 3 ]
};
var y = shallowClone(x);
console.log(y.b.f === x.b.f);     // true

Object.assign()

Object.assign() 方法可以把任意多个的源对象自身的可枚举属性拷贝给目标对象,然后返回目标对象。

var x = {
  a: 1,
  b: { f: { g: 1 } },
  c: [ 1, 2, 3 ]
};
var y = Object.assign({}, x);
console.log(y.b.f === x.b.f);     // true

深度拷贝

可以参考之前的一篇文章:javascript中的深拷贝和浅拷贝区分以及实现

方法1:使用 JSON 方法

JSON.stringify(array) 然后再 JSON.parse()。示例:

var arr1 = [1, 2, [3, 4], {a: 5, b: 6}, 7],
arr2 = JSON.parse(JSON.stringify(arr1));

console.log(arr1, arr2);
arr2[1] = 10;
arr2[3].a = 20;
console.log(arr1[1], arr2[1]);
console.log(arr1[3], arr2[3]);

此方法存在对古老浏览器的兼容性问题。如确需要作兼容,可引入如下兼容文件解决:

https://github.com/douglascrockford/JSON-js/blob/master/json2.js

注意:如果数组值为函数,上述方法还是不行的。

方法2:深度复制的完全实现

考虑到多维数组的嵌套,以及数组值为对象的情况,可以作如下原型扩展(当然写为普通函数实现也是可以的,原型扩展是不建议的方式):

Object.prototype.clone = function () {
var o = {};
for (var i in this) {
o[i] = this[i];
}
return o;
};
Array.prototype.clone = function () {
var arr = [];
for (var i = 0; i < this.length; i++)
if (typeof this[i] !== 'object') {
arr.push(this[i]);
} else {
arr.push(this[i].clone());
}
return arr;
};

//测试1 Object
var obj1 = {
name: 'Rattz',
age: 20,
hello: function () {
return "I'm " + name;
}
};
var obj2 = obj1.clone();
obj2.age++;
console.log(obj1.age);

//测试2 Array
var fun = function(log) {console.log},
arr1 = [1, 2, [3, 4], {a: 5, b: 6}, fun],
arr2 = arr1.clone();

console.log(arr1, arr2);

arr2[2][1]= 'Mike';
arr2[3].a = 50;
arr2[4] = 10;
console.log(arr1, arr2);

方法3:使用 jQuery 的 extend 方法

如果你在使用 jQuery,那么最简单的方法是使用 extend 插件方法。示例:

var arr1 = [1, 2, [3, 4], {a: 5, b: 6}, 7],
arr2 = $.extend(true, [], arr1);

console.log(arr1, arr2);
arr2[1] = 10;
console.log(arr1, arr2);


返回列表

想和你做个朋友

DO U LIKE?