实现深拷贝原理的递归方法:遍历对象,数组甚至内部都是基本的数据类型
这是第一种,还算是比较简单。注释都比较详细了,这里就不多加赘述
//实现深拷贝原理的递归方法:遍历对象,数组甚至内部都是基本的数据类型,然后复制它们,即深度复制
var obj = {
// 原数据,包含字符串,对象,函数,数组等不同类型
name:"test",
main:{
a:1,
b:2
},
fn:function(){
},
freinds:[1,2,3]
}
function copy(obj){
let newobj = null;//声明一个变量用来储存拷贝以后的内容
// 判断数据类型是否是复杂类型,如果是则调用自己,再次循环,如果不是,直接赋值即可
// 由于null不可以循环但循环又是object,所以需要对null进行判断
if(typeof(obj)==='object'&&obj !==null){
// 声明一个变量用来存储拷贝出来的值,根据参数的不同类型声明不同的值来进行存储
newobj = obj instanceof Array?[]:{}
// 循环obj中的每一个项,如果里面还有复杂的数据类型,则进行递归
for(var i in obj){
newobj[i] = copy(obj[i])
}
}
else{
newobj = obj
}
console.log('77',newobj)
return newobj;//函数必须要有返回值,否则结构为undefined
}
var obj2 = copy(obj)
obj2.name = 'test1'
obj2.freinds[2] = 100
obj2.main.b = 1000
console.log(obj)
console.log(obj2)
递归处理,引用类型,保持数据类型
第二种递归深拷贝,Object.create(null),初始化一个新对象
//2.递归处理,引用类型,保持数据类型
// 2.递归处理 引用类型 保持数组类型
function deepCopy(target){
if(typeof target ==='object'){
const newTarget = Array.isArray(target)?[]:Object.create(null)
for(const key in target){
newTarget[key] = deepCopy(target[key])
}
return newTarget
}
else{
return target
}
}
这里着重说一下,其实第一个递归类型就是不管三七二十一,不管是基本类型还是引用类型统统一股脑都递归,这样其实对于优化是不利的,基本数据类型其实可以不需要递归的,第二种的代码就做了修改,只针对引用类型,
const newTarget = Array.isArray(target)?[]:Object.create(null)
用的很妙,怎么说呢。如果这个object类型是数组类型,那么则让他继续保持着,如果不是的话则创建一个新对象,使newTarget终有所依,注意在递归里面一定要返回。然后不断的赋值给newTarget
哈希表Map支持循环引用
function deepCopy(target, h = new Map) {
if (typeof target === 'object') {
if (h.has(target)) return h.get(target)
const newTarget = Array.isArray(target) ? [] : Object.create(null)
for (const key in target) {
newTarget[key] = deepCopy(target[key], h)
}
h.set(target, newTarget)
return newTarget
} else {
return target
}
}
//这段代码实现了一个深拷贝函数,用于将一个对象或数组进行深拷贝,生成一个全新的对象或数组,以避免修改原始对象或数组时产生副作用。
具体来说,这个函数接受两个参数:要拷贝的目标对象或数组 target,以及一个记录已经拷贝过的对象的 Map 对象 h,默认值为一个新的 Map 对象。
首先,函数会判断目标对象的类型是否为对象(包括数组和普通对象),如果不是对象类型,则直接返回目标对象本身,因为基本数据类型的值是不可变的,不需要拷贝。
如果目标对象是对象类型,则进入递归拷贝的过程。首先会判断当前目标对象是否已经被拷贝过(即是否已经在 Map 对象 h 中出现过),如果是,则直接返回已经拷贝过的对象,避免无限递归拷贝。
如果目标对象还没有被拷贝过,则创建一个新的对象或数组 newTarget,具体是数组还是普通对象由目标对象的类型决定。接着,通过 for-in 循环遍历目标对象的所有属性,并将每个属性值递归调用深拷贝函数,将拷贝后的值存储在新的对象或数组 newTarget 中。
最后,将目标对象和新的对象或数组 newTarget 存储在 Map 对象 h 中,以便下次遇到相同的目标对象时可以直接返回已经拷贝过的对象。最后,返回新的对象或数组 newTarget。
总的来说,这个深拷贝函数实现了递归遍历目标对象,将所有属性都进行了拷贝,并且避免了循环引用的问题,是一个比较完备的深拷贝函数实现。
使用weakMap来实现递归(好处,有垃圾回收机制,WeakMap中的键必须是对象,而且当键对象不再被引用时,WeakMap会自动清除)
function deepCopy(target, h = new WeakMap) {
if (typeof target === 'object') {
if (h.has(target)) return h.get(target)
const newTarget = Array.isArray(target) ? [] : Object.create(null)
for (const key in target) {
newTarget[key] = deepCopy(target[key], h)
}
h.set(target, newTarget)
return newTarget
} else {
return target
}
}
//
这段代码实现了一个深拷贝函数,与前面的代码相比,使用了 WeakMap 替代了 Map。WeakMap 和 Map 的使用方法类似,但是与 Map 不同的是,WeakMap 中的键必须是对象类型,而且当键对象不再被引用时,WeakMap 会自动清除该键值对,以便垃圾回收。
与前面的代码类似,这个深拷贝函数接受两个参数:要拷贝的目标对象或数组 target,以及一个记录已经拷贝过的对象的 WeakMap 对象 h,默认值为一个新的 WeakMap 对象。
函数的核心逻辑与前面的代码基本一致,不再赘述。区别在于,这个函数使用 WeakMap 来记录已经拷贝过的对象,而不是使用 Map。使用 WeakMap 的好处是可以自动清除不再被引用的对象,避免内存泄漏。