一、深浅拷贝的概念
1. 假设B复制了A,当修改B时,看A是否会发生变化,如果A也跟着变了,说明是浅拷贝,如果A没变,说明是深拷贝。
即,深拷贝:所有属性都被拷贝,两者相互独立,互不影响;浅拷贝:并非所有属性都被拷贝,两者互相影响。
2. JS中的数据类型分为基本数据类型和引用数据类型。
(1)基本数据类型不存在深浅拷贝,两者相互独立,互不影响。
当 var a = 1,var b = a 时,相当于在栈内存中新开辟了一个内存,如下所示:
(2)引用数据类型的浅拷贝,两者互相影响。
当 var a = [0,1,2,3,4],var b = a 时,a 和 b 会指向同一个堆地址,所以当我们改变 a 时 b 也会改变,改变 b 时 a 也会改变,如下所示:
(3)引用数据类型的深拷贝,两者相互独立,互不影响。
在深拷贝的情况下,我们会为 b 单独开辟一块堆内存,如下所示:
3. 深浅拷贝的概念只针对较为复杂的引用数据类型(对象类型)。
二、浅拷贝的实现
浅拷贝直接用 “=” 赋值即可。
三、深拷贝的实现
1. 迭代递归法(只能实现object,array的深拷贝)
(1)for...in...法
对对象进行迭代操作,对它的每个值进行递归深拷贝。
function isObject(o) {
return (typeof o === 'object' || typeof o === 'function') && o !== null
}
// 迭代递归法:深拷贝对象与数组
function deepClone(obj) {
if (!isObject(obj)) {
throw new Error('obj 不是一个对象!')
}
let isArray = Array.isArray(obj)
let cloneObj = isArray ? [] : {}
for (let key in obj) {
cloneObj[key] = isObject(obj[key]) ? deepClone(obj[key]) : obj[key]
}
return cloneObj
}
(2)Reflect 法
function deepClone(obj) {
if (!isObject(obj)) {
throw new Error('obj 不是一个对象!')
}
let isArray = Array.isArray(obj)
let cloneObj = isArray ? [...obj] : { ...obj }
Reflect.ownKeys(cloneObj).forEach(key => {
cloneObj[key] = isObject(obj[key]) ? deepClone(obj[key]) : obj[key]
})
return cloneObj
}
拓展知识点:(1)ES6 Reflect (2)扩展运算符( ... )
2. 序列化反序列化法(只能实现object,array的深拷贝)
使用JSON对象的 parse 和 stringify 方法来实现深拷贝。通常应用于开发中,不用考虑除对象和数组以外的情况。
function deepClone(obj){
let _obj = JSON.stringify(obj),
objClone = JSON.parse(_obj);
return objClone
}
// 测试
let a = [0,1,[2,3],4],
b = deepClone(a);
b[0] = 1;
b[2][0] = 1;
console.log(a,b);
// a: [ 0, 1, [ 2, 3 ], 4 ]
// b: [ 1, 1, [ 1, 3 ], 4 ]
3. lodash中深拷贝的实现
著名的 lodash 中的 cloneDeep 方法同样是使用 Reflect 方法实现的,只不过它支持的对象种类更多,具体的实现过程读者可以参考 lodash 的 baseClone 方法。lodash可以完成 object、array、date、regexp 的深拷贝,但 function 和 error 仍然不行。
参考:
END