关于数组、对象、对象数组的深拷贝
浅拷贝和深拷贝一般是相对于内存指向来讲的,对于数组和对象,如果直接使用赋值运算符,这只是把数组或对象的内存地址赋值给了新的变量,比如:
1
2
3
let origin = [1, 2, 3]
let target = origin
target[0] = 10 // origin => [10, 2, 3]
可以看到,操作新变量和操作原变量都是在操作同一块内存空间,所以浅拷贝在某些场景下是不适用的。
普通数组的深拷贝
首先提出普通数组指的是,数组元素都不是数组,对象这些引用类型,这种形式的数组我们用下面这些方法来拷贝
1
2
3
4
5
6
7
8
9
10
target = origin.slice()
target = [].concat(origin)
target = [...origin]
const deepClone = (origin) => {
let target = []
for (let i in origin) {
target[i] = origin[i]
}
return target
}
对于多维数组(数组中嵌套数组),可以使用递归调用深拷贝的方式来进行拷贝。
1
2
3
4
5
6
7
const deepClone = (origin) => {
let target = []
for (let i in origin) {
target[i] = Array.isArray(origin[i]) ? deepClone(origin[i]) : origin[i]
}
return target
}
对象的深拷贝
对于没有嵌套的对象,我们仍可以用遍历属性的方式去拷贝,遍历可以用for...in, Object.keys(obj)
等方法;另外,也可以通过扩展运算符来实现拷贝对象。
1
2
3
4
5
6
7
8
9
10
const deepClone = (origin) => {
let target = {}
for (const i in origin) {
if (origin.hasOwnProperty(i)) {
const element = origin[i];
target[i] = element
}
}
return target
}
对于对象内的嵌套,也可以使用递归来实现深拷贝。
1
2
3
4
5
6
7
8
9
10
const deepClone = (origin) => {
let target = {}
for (const i in origin) {
if (origin.hasOwnProperty(i)) {
const element = origin[i];
target[i] = typeof element === 'object' ? deepClone(element) : element
}
}
return target
}
对象数组嵌套的深拷贝
在上面的代码中,添加判断是数组还是对象,然后递归调用即可。
1
2
3
4
5
6
7
8
9
10
const deepClone = (origin) => {
let target = Array.isArray(origin) ? [] : {}
for (const i in origin) {
if (origin.hasOwnProperty(i)) {
const element = origin[i];
target[i] = typeof element === 'object' ? deepClone(element) : element
}
}
return target
}
骚操作
在MDN的Object.assign()
方法上看到的,可以利用JSON
对象先将数组对象转成字符串,再将字符串编译成数组对象,这样转换之后得到的就是新的数组对象了;不过方法也有局限性,不能复制函数,新的对象只是一个Object
,不再有继承性。MDN: Object.assign()
let target = JSON.parse(JSON.stringify(origin))