JavaScript 中深复制与浅复制的实现

前言

在 JavaScript 中,对于 Object 与 Array 等引用类型值的复制有深复制与浅复制的区别。简单的说,浅复制只复制一层对象的属性,而深复制则递归复制了所有层级。下面介绍一下如何实现对象的深复制与浅复制。

浅复制

浅复制只会将对象的各个属性依次复制,并不会进行递归复制,它很容易实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//浅复制
const shallowCopy = source => {
//检验传入的参数
if (!source || typeof source !== 'object') {
throw new Error('Error arguments');
}
//判断参数是数组还是普通对象
let newObj = source.constructor === Array ? [] : {};
//遍历 source 对象的属性,并赋值给 newObj 对象
Object.keys(source).forEach(key => {
newObj[key] = source[key];
});
return newObj;
};

对于数组,我们还可以通过 Array.prototype.slice()Array.prototype.concat() 来进行浅复制。如下所示:

1
2
3
4
5
6
7
let arr1 = ['szm', {age: 24}];
//浅复制
let arr2 = arr1.slice();
let arr3 = arr1.concat();
console.log(arr2);
console.log(arr3); // ['szm', {age: 24}]

在 ES6 中,Object 类型引入了一个新的方法 Object.assign(),用于将所有可枚举属性的值从一个或多个源对象复制到目标对象,所以我们可以利用它来进行对象的浅复制。如下所示:

1
2
3
4
5
6
7
8
let obj = {
name: 'szm',
age: 24
};
//浅复制
let copy = Object.assign({}, obj);
console.log(copy); // { name: 'szm', age: 24 }

深复制

不同于浅复制,深复制会将对象的属性递归复制,其实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//深复制
const deepCopy = source => {
//检验传入的参数
if (!source || typeof source !== 'object' ) {
throw new Error('Error arguments');
}
//判断参数是数组还是普通对象
let newObj = source.constructor === Array ? [] : {};
//遍历 source 对象的属性,并递归赋值给 newObj 对象
Object.keys(source).forEach(key => {
if (source[key] && typeof source[key] === 'object') {
newObj[key] = source[key].constructor === Array ? [] : {};
newObj[key] = deepCopy(source[key]);
} else {
newObj[key] = source[key];
}
});
return newObj;
};

除了递归,还有一种实现深复制的方式是利用 JSON 对象的 parse() 方法和 stringify() 方法,JOSN 对象中的 stringify 可以把一个 js 对象序列化为一个 JSON 字符串,parse 可以把 JSON 字符串反序列化为一个 js 对象,通过这两个方法,也可以实现对象的深复制。

但是,由于 JSON 对象不支持 undefined 类型与函数,所以它们在序列化时会被有意忽略。如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//利用JSON序列化实现一个深复制
const deepCopy = source => JSON.parse(JSON.stringify(source));
let source = {
arr: [1, 2, 3],
obj: {
name: 'szm'
},
func: function(){
return 1;
}
};
let target = deepCopy(source);
console.log(target); // {arr: [1,2,3], obj: {name: 'szm'}}
------ 本文结束 ------
坚持原创技术分享,您的支持将鼓励我继续创作!