深拷贝和浅拷贝深究

144 阅读2分钟

在理解深拷贝和浅拷贝之前,咋们先了解下JavaScript有那些类型:

基本数据类型:Number、String、Boolean、undefined、Null、以及Es6新特性Symbol、或者未来Es10中的Bigint,一共是7种。

引用数据类型:Object,又细分为Object、Array、function等等

简单来讲,一共是8种数据类型;

接下来我们讲讲基本和引用数据类型的存储原理:

基本数据类型:存储方法是直接将数据存储在栈中。 引用数据类型:存储方式是先在栈中开辟一个空间也就是咋们常说的入栈,存入一个地址,然后这个地址是指向堆中的某个位置,数据实际是存放在堆中。

造成这种情况的原因是因为在赋值的过程中,仅仅是将栈中的地址进行了赋值,并没有在堆中给newobj重新开辟一个新的空间。导致obj和newobj指向同一个地址,这才是导致问题的所在。所以就导致了现在的很明显的旧数组修改了里面的某个字段,新的数组也跟着变了,这明显是不合理的。

浅拷贝代码如下

function shallowClone(obj){
	let res = {}
	for(let i in obj){
		res[i] = obj[i]
	}
	return res
}

这是不符合我们的要求的,进入深拷贝实战

1,烂大街的乞丐版

function normalVersion(obj){
	return JSON.parse(JSON.stringify(obj))
}

存在两个问题:存在函数无法复制 和循环引用问题 比如原对象有字段是函数就无法复制过去,循环引用就是对象中两个字段互相调用,你中有我,我中有你就爆炸了

2,深拷贝普通版 还是存在循环引用问题

function deepClone(obj){
	let res = null
	if(typeof obj ==='object'){
		res = (obj instanceof Array) ? []: {}
		for(let i in obj){
			res[i] = typeof obj[i] ==='object'? deepClone(obj[i]) : obj[i]
		}
	}else{
		res = obj
	}
	return res
}

3,深拷贝加强版 解决循环引用问题

//深拷贝加强版  解决循环引用问题
function deepCloneVersion2(obj,map=new Map()){
	let res = null
	if(typeof obj ==='object'){
		//克隆之前进行判断数据之前是否克隆过
		let cache = map.get(obj)
		if(cache){
			return cache
		}
		res = (obj instanceof Array) ? []: {}
		map.set(obj,res)
		for(let i in obj){
			res[i] = typeof obj[i] ==='object'? deepCloneVersion2(obj[i],map) : obj[i]
		}
	}else{
		res = obj
	}
	return res
}

调用例子

	a:'a',
	b:{
		c:'c',
		d:{
			e:'e',
			f:['f']
		}
	},
	h:[1,2,3],
	g:function(){}
}

source.h.push(source.b)
source.b.new = source.h
let target = deepCloneVersion2(source)
console.log('target',target)
console.log('source',source)