js-- 堆栈内存关系
1、栈(stack)和堆(heap)
stack为自动分配的内存空间,它由系统自动释放;
而heap则是动态分配的内存,大小不定也不会自动释放。
2、基本类型和引用类型
基本类型:存放在栈内存中的简单数据段,数据大小确定,内存空间大小可以分配。
5种基本数据类型有Undefined、Null、Boolean、Number 和 String,它们是直接按值存放的,所以可以直接访问。
引用类型:存放在堆内存中的对象,变量实际保存的是一个指针,这个指针指向另一个位置。每个空间大小不一样,要根据情况开进行特定的分配。
当我们需要访问引用类型(如对象,数组,函数等)的值时,首先从栈中获得该对象的地址指针,然后再从堆内存中取得所需的数据。
3、传值与传址
var a = [1,2,3,4,5]; var b = a; var c = a[0]; alert(b);//1,2,3,4,5 alert(c);//1 //改变数值 b[4] = 6; c = 7; alert(a[4]);//6 alert(a[0]);//1
a-b:传址 a-c:传值
3、浅拷贝
前面已经提到,在定义一个对象或数组时,变量存放的往往只是一个地址。当我们使用对象拷贝时,如果属性是对象或数组时,这时候我们传递的也只是一个地址。因此子对象在访问该属性时,会根据地址回溯到父对象指向的堆内存中,即父子对象发生了关联,两者的属性值会指向同一内存空间。
let a = { key1: 111 } function shallowCopy(p){ let c = {} for(let i in p){ c[i] = p[i] } return c } a.key2 = ['a','b'] let b = shallowCopy(a) b.key3 = 333 console.log('a', a) /** a:key1:111 key2:['a','b'] */ console.log('b', b) /** b:key1:111 key2:['a','b'] key3:333 */ /**浅拷贝: 为b添加新属性,并未影响到a*/ /**修改一下a的数组*/ b.key2.push('c') console.log('a.key2',a.key2) /** a.key2:['a','b','c']
a中key2也发生了改变 */
原因是key1的值属于基本类型,所以拷贝的时候传递的就是该数据段;但是key2的值是堆内存中的对象,所以key2在拷贝的时候传递的是指向key2对象的地址,无论复制多少个key2,其值始终是指向父对象的key2对象的内存空间。
4、深拷贝
或许以上并不是我们在实际编码中想要的结果,我们不希望父子对象之间产生关联,那么这时候可以用到深拷贝。既然属性值类型是数组和或对象时只会传址,那么我们就用递归来解决这个问题,把父对象中所有属于对象的属性类型都遍历赋给子对象即可。
let a = {
key1: 111
}
a.key2 = ['a','b']
function deepCopy(p,c){ let c2 = c ||{}; for(let i in p ){ if(typeof p[i] === 'object'){ c2[i] = (p[i].constructor === Array) ? [] : {} deepCopy(p[i],c2[i]) }else{ c2[i] = p[i] } } return c2 } let d = {} d = deepCopy(a , d) d.key2.push('d') console.log('a.key2',a.key2) //a.key2: ["a", "b", "c"] console.log('d.key2',d.key2) //d.key2: ["a", "b", "c", "d"]
/**新生成的子对象并没有影响父对象*/
如若喜欢,欢迎转载,请在文章页面明显位置给出此文链接!
若您觉得这篇文章还不错请点击下右下角的推荐,有了您的支持才能激发作者更大的写作热情,非常感谢!