Js 深拷贝

Posted by Youzi Blog on June 18, 2019

关于数组、对象、对象数组的深拷贝

浅拷贝和深拷贝一般是相对于内存指向来讲的,对于数组和对象,如果直接使用赋值运算符,这只是把数组或对象的内存地址赋值给了新的变量,比如:

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))