本文档适用于有一定经验的开发者。我们默认你已经掌握了指针的概念(也可称为地址) ,如果你是新手,你可能需要先了解 指针的概念
我们在JS中经常需要复制一个对象或数组。
当我们直接使用
=赋值时
,我们实际只赋值了这个对象的指针。这种情况 连拷贝都算不上。
例:如下我们给array2赋值,实际是赋值给array2一个指针,指针指向array1指向的数组。因此我们编辑 array2 时,array1也会变。
let array1 = [1,2,3]
let array2 = array1
array2[0] = 100
/*
array1
>(3) [100, 2, 3]
array2
(3) [100, 2, 3]
*/
浅拷贝可以将数组(对象)的第一层拷贝下来。
ES6给我们提供了两个方便的浅拷贝方法:
Array.from()与Object.assign()
例:下面我们通过Array.from()
对array3进行浅拷贝。我们可以看出,当我们修改array4的普通元素时,array3已经不会随着改变了。但是浅拷贝只是拷贝了第一层的元素,如果第一层元素含有对象(数组),我们只拷贝一个指针(地址)。array4通过这个指针(地址)访问的对象将是与array3是相同的。
let array3 = [1,2,3,{a:1,b:2,c:3}]
let array4 = Array.from(array3)
array4[0] = 100
array4[3].a = 100
/*
array3
>(4) [1, 2, 3, {a: 100, b: 2, c: 3}]
array4
>(4) [100, 2, 3, {a: 100, b: 2, c: 3}]
*/
目前JS常见的深拷贝有两种方式
。第一种种是两次JSON化:
let array6 = JSON.parse(JSON.stringfiy(array5))
这种方法很简单,但是当元素含有undefined
或正则表达式
时函数会抛出异常。
第二种深拷贝是逐层遍历逐一赋值的方法,要麻烦一些,但是没有第一种的缺点。
目前,这两种以外的拷贝方式都是浅拷贝。例如for...of遍历中的item实际是对对象元素的浅拷贝。
// 例:逐层遍历逐一赋值的深拷贝工具类
class deepCopy {
static toRawType(value) {
return Object.prototype.toString.call(value).slice(8, -1);
}
/* 已整合在tools中 */
static copy(obj) {
let emptyObject = null;
if (this.toRawType(obj) == 'Object') {
emptyObject = {};
}
else if (this.toRawType(obj) == 'Array') {
emptyObject = [];
}
else {
return obj;
}
for (let key in obj) {
if (typeof obj[key] != 'object' || obj[key] == null) {
emptyObject[key] = obj[key];
}
else {
emptyObject[key] = this.copy(obj[key]);
}
}
return emptyObject;
}
}
let array5 = [1,2,3,{a:1,b:2,c:3}]
let array6 = deepCopy.copy(array5)
array6[3].a = 100
/*
array3
>(4) [1, 2, 3, {a: 1, b: 2, c: 3}]
array4
>(4) [1, 2, 3, {a: 100, b: 2, c: 3}]
*/