深拷贝和浅拷贝的区别?怎么实现深拷贝?
浅拷贝:只复制一层对象的属性【扩展运算符{...obj}
、Object.assign({}, obj)
】
数组的浅拷贝:
Array.from(arr)
、[…arr]
、arr.concat()
、arr.slice()
深拷贝 :会递归地复制对象的所有层级的属性【递归函数、Json.parse(Json.stringify(obj))
】
Json.parse(Json.stringify(obj)) 局限:不能复制函数、循环引用
区别
-
对象的属性是基本类型时:深拷贝和浅拷贝都是拷贝一个副本,拷贝后修改新对象不会影响原始对象;
-
对象的属性是引用类型时:浅拷贝只拷贝内存地址,不拷贝引用地址指向的数据 - 修改新对象会影响原始对象;深拷贝则会拷贝数据,修改新对象不会影响原始对象;
浅拷贝的实现
1.Object.assign():把任意多个的源对象自身的 可枚举属性 拷贝给目标对象,然后返回目标对象
2.Array.prototype.concat():合并两个或多个数组,不传参数时相当于浅拷贝
let arr = [1, 2, {username: 'aQiu'}];
let arr2 = arr.concat();
arr2[2].username = 'Alice';
console.log(arr); // [1, 2, {username: 'Alice'}] 变了
3.Array.prototype.slice(start, end):由 start 和 end 决定的数组的浅拷贝
深拷贝的实现
JSON.parse(JSON.stringify())
:用JSON.stringify
将对象转成JSON字符串,再用JSON.parse()
把字符串解析成对象,一去一来,新的对象产生了,可以实现数组或对象深拷贝,但不能处理函数、循环引用。
let arr = [1, 3, {username: 'aQiu'}];
let arr2 = JSON.parse(JSON.stringify(arr));
arr2[2].username = 'Alice';
console.log(arr); // [1, 3, {username: 'aQiu'}] 不变
- 递归:递归方法实现深度克隆原理:遍历对象、数组直到里边都是基本数据类型,然后再去复制,就是深度拷贝。
// 利用 for 循环可以简单实现为
function deepCopy(target) {
// 判断对象是引用数据还是基础数据 - 基础数据直接返回
if (typeof target !== 'object' || target === null) {
return target
}
let copy;
if (Array.isArray(target)) {
copy = [];
for (let i = 0; i < target.length; i++) {
copy[i] = deepCopy(target[i]);
}
} else {
copy = {};
for (let key in target) {
if (target.hasOwnProperty(key)) {
copy[key] = deepCopy(target[key]);
}
}
}
return copy;
}
// 自己写的 - reduce 的问题在于 [[1,2,3],4,5,6] 数组嵌套数组时,被打平
// 数组
if (Array.isArray(target)) {
return target.reduce((pre, cur) => {
return pre.concat(deepCopy(cur))
}, []);
} else {
// 对象
return Object.keys(target).reduce((pre, cur) => {
return Object.assign(pre, { [cur]: deepCopy(target[cur]) })
}, {})