Let a={id:1} let b=a b.id=2 console.log(a,b)
最终结果a,b的值都等于{id:3},这个称之为浅拷贝。也就是一个值改变另外一个值也跟着改变。
我们把上面的代码再改一下如下
let a={id:1} let b= {} for (let i in a){ b[i]=a[i] } b.id=3 console.log(a,b)
最终得到的结果{id: 1} {id: 3},改变b的值而a不受影响这个就叫做深拷贝。可以理解为通过样本a重新建立了一个b。
深拷贝和浅拷贝的原理
这个就涉及到内存的概念了,内存有栈和堆它们都是用来存放数据的,栈用来存放一些系统类型,比如字符串和数字。堆用来存放复杂数据。浅拷贝以下图示例它们的关系如我下方的涂鸦
浅拷贝如上图a和b都同时指向了value。所以改变a和b都能改变value。
这个深拷贝如上图b继承了a的一切,然后重新再堆里另外开辟了一个空间放置数据。所以a和b它们的指向空间都是不同的。
深拷贝和浅拷贝只出现再引用类型上
上面的例子我们为什么用JSON数据来做示例,原因是值类型(string number null bool boolean undefined)它们的值都存放在内存栈当中,每一个值类型的值都是存放在独立的栈空间种。用代码说话
let a=1 let b=a b=2 console.log(a,b)
打印的结果就是1,2.下图表示它们的关系
当let b=a的时候就已经在内存栈当中开辟了一个空间来存放它的值了。所以深拷贝和浅拷贝只存在于引用类型上(对象(Object)、数组(Array)、函数(Function))
为什么要使用深拷贝
浅拷贝我们就不说了,很简单,直接a=b没烦扰。但为什么要使用深拷贝呢?这个有的时候是为了避免数据污染。比如我们有的时候只想改变b而a不变,这个时候就需要用到深拷贝了。
深拷贝有哪些方法
如果是数据都能用json转换如下面的代码类型
let a=[{id:1},{s:"我是字符"}] let b=[1,2,3,4] let c=["a","b"]
那很简单,直接通过JSON.parse(JSON.stringify(数据))就可以得到想要的数据了。
另外一种是通过递归,通过逐级的判断值如果不是值类型就继续递归自身来逐级的添加数据到新的变量里。我这刚好有个实际项目中的递归代码如下
function q(s) { //递归函数 let t; if (typeof s === 'object') { t = Array.isArray(s) ? [] : {} for (let i in s) { if (s.hasOwnProperty(i)) { if (typeof s[i] !== 'object') { t[i] = s[i] } else { t[i] = q(s[i]) } } } } else { t = s } return t } let a={obj:[{sid:1}]} let b=q(a); b.obj[0].sid=2 console.log(a,b)
打印结果如下
当然上述那个递归是不能递归函数的,因为没加函数的判断。还有一些js原生方法如Object.assign,但它们只能深拷贝一层比如{b:"a"} 这样一层的数据,如果是{b:{c:{a:["2"]}}} 这样的多层数据就没卵用了,所以推荐使用递归的方法。数据格式允许的情况下使用JSON.parse肯定是最方便的。