老规矩先出一道题目,大家猜猜打印的结果是什么,然后我们一起探讨背后的原因。
var a=[1,2,3]var b=a;var c=[6,7,8]b=cb[0]=9a=cconsole.log(a)console.log(b)console.log(c)
先分析题目,我们会看到b首先对a进行了一次复制,接下来,b又复制了c,
最后b又改变其中的值。最后打印出a,b,c看看它们的结果有没有什么关联。
第一步:了解栈(stack)堆(heap)的概念,看看我们的a,b,c存在哪里。
stack为自动分配的内存空间,它由系统自动释放;而heap则是动态分配的内存,大小不定也不会自动释放。
第二步:存放在堆内存中的对象,变量实际保存的是一个指针,这个指针指向另一个位置。每个空间大小不一样,要根据情况开进行特定的分配。
当b=a时,b这个时候和a指向的是同样的地址,即此刻b=[1,2,3]
第三步:当堆中出现c的值,并且b=c,这个时候就是b复制c的值,b和c指向同一个地址。此时b的值为[6,7,8]
第四步:当b的值改变,b[0]=9,此时堆中的值为[9,7,8],此时c的值也为[9,7,8]
第五步:a复制c,此时a和b和c指向同一个地址。我们也会发现一个问题,那就是原来a中的值似乎还是存在堆中,
系统无法对它回收,但是我们却不知道通过什么方式找到它,但是它又占用了堆中的内存,这个就是内存泄露。
上面的就是一个典型的浅拷贝实现过程,拷贝对象里面的属性改变会影响原对象。那什么是深拷贝呢?深拷贝就是
拷贝对象里面的属性改变不会影响原对象的属性。
那如何实现一个深拷贝呢?
var array=[1,2,3]function copy(array){ let newArray=[] for(let item of array){ newArray.push(item) } return newArray}var copyArray=copy(array)copyArray[0]=100console.log(array)console.log(copyArray)
第一步:
第二步:newArray会复制array中的值,不过此时newArray中的值和array的值不在同一个堆中
第三步:newArray中的值改变了,但是array中的值不会改变。
同理我们可以实现对象的深拷贝
var newObject={}var obj={ name:'a', b:'student'}function copy(obj){ for(let item in obj){ newObject[item]=obj[item] } return newObject}var copyObj=copy(obj)copyObj.name='b'console.log(obj);//{name:'a',b:'student'}console.log(copyObj)//{name:'b',b:'student'}
如果实现一个针对数组和对象的深拷贝是如何实现的呢?
function deepClone(s,t){ var t={}||[] for(var i in s){ if(typeof s[i]==='object'){ t[i]=(s[i].constructor===Array)?[]:{} deepClone(s[i],t[i]) }else{ t[i]=s[i] } } return t}a.name={a:'aa'}var b={}b=deepClone(a,b)b.name={a:'bb'}console.log(a.name)console.log(b.name)
如果不是单独针对数组或对象,而是能够通过数组对象和其他复杂的JSON形式的对象,我们怎么实现深拷贝呢?
var array=[ {name:1}, {name:2}, {name:3}]var copyArray=JSON.parse(JSON.stringify(array))copyArray[0].name=100console.log('aa',array)console.log('bb',copyArray)