整理前理解口述
深浅拷贝问题的出现是js对不同类型的存储方式而引发的;
对于原始数据类型, 它们的值是直接存储在栈内存中; 而复杂数据类型, 栈内存中记录的是它的指针, 而指针指向堆内存中真正的值;
所以对于原始数据类型就没有深浅拷贝一说;
而对于复杂数据类型, 浅拷贝就是仅复制指针, 但被复制对象改变时, 新复制的对象也会跟着改变; 深拷贝则是连同堆内存中的数据完全拷贝一份, 新旧对象的变化互不影响;
什么是深浅拷贝
浅拷贝:拷贝复杂类型时,仅拷贝对象的指针;当原对象改变时,拷贝对象也会跟着改变
深拷贝:拷贝复制类型时,拷贝对象的值;当原对象改变时,拷贝对象不会跟着改变
深浅拷贝的实现方案
浅拷贝方案:
- …展开运算符(ES6)
- obj.assign()
- contact
- …
深拷贝:
- 手写递归
- JSON.parse(JSON.stringfy())
- 先转换成字符串, 再解析回obj
- 缺点是不能处理正则\函数等特殊数据类型
- 一些库封装的深拷贝方法, 例: loadsh的_.cloneDeep()
手写实现
// 先写一个浅拷贝
function _deepClone(target) {
let cloneRes = {};
for(const key in target) {
cloneRes[key] = target[key]
}
return cloneRes
// 函数出口
// if (typeof target !== 'obejct') return target
}
// 2. 普通深拷贝(只考虑对象/非对象)
function _deepClone(target) {
if (typeof target === 'object'){
// 如果是对象, 进行处理
let cloneRes = {};
for(const key in target) {
cloneRes[key] = _deepClone(target[key])
}
return cloneRes
} else {
// 如果不是对象, 直接返回
return target
}
}
// 3.考虑数组的深拷贝
function _deepClone(target) {
if (typeof target === 'object') {
let cloneRes = Array.isArray(target) ? [] : {}; // 核心
for (const key in target) {
cloneRes[key] = _deepClone(target[key])
}
return cloneRes
} else {
return target
}
}
// 4. 考虑循环引用
// Q:为什么要用WeakMap替代Map?
// A:强引用和弱引用的区别,强引用只能手动释放,弱引用能够被垃圾回收机制释放,使用Map会造成内存额外消耗。
// WeakMap中的键是弱引用,Map中的键是强引用
function _deepClone(target, map=new WeakMap()) {
if (typeof target === 'object') {
let cloneRes = Array.isArray(target) ? [] : {}; // 核心
// 防止循环调用导致的栈内存溢出
if (map.get(target)) {
return map.get(target)
}
map.set(target, cloneRes)
for (const key in target) {
cloneRes[key] = _deepClone(target[key], map)
}
return cloneRes
} else {
return target
}
}
// 5. 考虑其他数据类型
function _deepClone (target, map = new WeakMap()) {
if (target === null) return null
if (target instanceof Date) return new Date(target)
if (target instanceof RegExp) return new RegExp(target);
if (typeof target !== 'object') return target
if (map.get(target)) return map.get(target);
let cloneRes = new target.constructor()
map.set(target, cloneRes)
for (const key in target) {
if (target.hasOwnProperty(key)) {
// map.set(key, target[key])
cloneRes[key] = _deepClone(target[key], map);
}
}
return cloneRes
}
// 6. 性能优化
// 把for..in改为普通的for循环,性能会有所提高
// 测试案例
const target = {
field1: 1,
field2: undefined,
field3: {
child: 'child'
},
field4: [2, 4, 8]
};
const newTarget = _deepClone(target)
newTarget.field4[1] = "Child2"
console.log(target);
console.log(newTarget);
console.log(target === newTarget);
// 输出:
// {
// field1: 1,
// field2: undefined,
// field3: { child: 'child' },
// field4: [ 2, 4, 8 ]
// }
// {
// field1: 1,
// field2: undefined,
// field3: { child: 'child' },
// field4: [ 2, 'Child2', 8 ]
// }
// false