老规矩先出一道题目,大家猜猜打印的结果是什么,然后我们一起探讨背后的原因。
var a=[1,2,3]
var b=a;
var c=[6,7,8]
b=c
b[0]=9
a=c
console.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]=100
console.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=100
console.log(\'aa\',array)
console.log(\'bb\',copyArray)