目录
引言:我们在学习js过程中,也很想知道各种类型变量在内存中时如何存储的,今天我们讲学习这系列内容。
1.栈和堆
我们首先必须了解栈和堆两大概念,各种类型的变量就存储在这两者中
1.1栈结构及其相关存储属性
栈结构这里就不细讲了,详细大家之前学习中肯定了解栈结构的性质
在内存存储中,栈区中每一层叫做栈帧都占用栈本身有容量限制,超过限制为栈溢出
其中基本变量类型和数组地址值,对象的唯一访问地址值都存储在栈中
1.2堆结构及其相关属性
堆结构可以存储内存上连续的空间(数组),或者不连续的内存空间(对象)
1.3图解栈和堆
对于数组或者对象来说,堆内存中存储真实数据,然后地址值存储在栈中,需要访问他们时,只需要从栈中拿到地址值,然后拿着钥匙(地址值)去堆内存中拿数据
对于基本变量来说,就直接将真实数据存储到栈中
在实际开发中,这种存储方式也带来许多问题,下面来看一个案例:
var a= [1,2,3,4]
var b=1
var xx1=b
var x=a
console.log(xx1,x);//1,[1,2,3,4]
//当我们修改x的第一个值,然后打印原数组
var a= [1,2,3,4]
var b=1
var xx1=b
var x=a
console.log(xx1,x);//1,[1,2,3,4]
x[0]=9
console.log(a);//[9,2,3,4]
可以得到当操作xx1时,也会影响到,所以xx1和a在堆中指同一内存空间
但如果给xx1设置新的[1,2,3,4],及直接在xx1直接定义,两者将不会关联同一数组
将a=null时
只是将栈中的引用地址去掉,但是 堆内存中真实数据还会存在堆中,浏览器js引擎被垃圾回收地址收掉用来释放浏览器的运行内存
2.拷贝
什么是拷贝,我想从引用传递和拷贝的区别进行入手讲解:
先看下面代码
//引用传递
var arr=[1,2,3,4]
var arr1=arr
//拷贝
let arr2=[]
for(let i=0;i<arr.length;i++){
arr2.push(i)
}
//改变arr2
arr2[0]=3
console.log(arr2,arr);//arr[0]还是为0
引用传递和拷贝的区别:
引用传递两个数组访问的是堆内存中同一内存空间,而拷贝是在堆内存中重新申请一段内存,比如在案例中arr2和arr在堆中是两端不同的内存空间
正式介绍一下拷贝的概念,比如说数组拷贝相当于在内存中开辟一个新的内存空间,并且将原数组的所有内容风别按照下标复制一封放到新的数组,这样内存中会出现一个一样的数组,但是存储在不同的位置他们相互之间没有关系
但是这种方式为浅拷贝
深拷贝
我们来通过浅拷贝和深拷贝的区别来认识深拷贝
先来看一个浅拷贝:
var arr=[{name:"a",age:17},{name:'s',age:18}]
var arr1=[]
for(let i=0;i<arr.length;i++){
arr1.push(i)
}
arr1[0].name='uuu'
console.log(arr,arr1);//arr的第一个对象中的name属性也为uuu
arr1.push(123)
log(arr,arr1)//只有arr1有123
观察结果来看,我们的拷贝还是浅拷贝,在内部赋值时还是会出现引用传递,也也就是出现影响原数组的问题,所以浅拷贝不能将对象的所有属性都进行复制,要想办法实现深拷贝
1.利用JSON.parse和JSON.stringify实现深拷贝
var arr=[{name:"a",age:17},{name:'s',age:18,},address:undefined]
var arr1=[]
for(let i=0;i<arr.length;i++){
arr1.push(i)
}
arr1[0].name='uuu'
console.log(arr,arr1);//arr的第一个对象中的name属性也为uuu
//先将arr转为字符串
var str=JSON.stringify(arr)
//然后将字符串转为一个反序列化一个新数组
var arr3=JSON.parse(str)
//此时验证一下,当修改arr3第一个对象的name属性时,原arr中对应的属性值会不会受到影响
arr3[0].name='iii'
console.log(arr,arr3);//没影响,arr中name属性还是原来的值
这就是一个深拷贝,我们观察结果,arr中的name属性并没有受到影响,所以代表时成功的
但是这个方法也有缺点,当我们原数组对象中包含函数或者带有原型链的对象 时,会导致数据丢失,比如说我们在原数组上添加一个函数,但是通过一系列操作后,会发现str和arr3都没有这个函数,
缺点二:观察结果,address这个属性不能被复制,即不能有undefined
第二种深拷贝的方式
var myobj={
name:'kerwin',
arr:[1,2,3]
}
var myobj2={
...myobj
}
myobj2.name="xiaoming"
myobj2.arr.splice(1,1)
console.log(myobj);//通过控制台打印我们发现
通过控制台的打印,我们发现,此时myobj被影响到,此种拷贝方式其实只比拷贝多拷贝一层
在实际开发中我们都借助第三方库来实现(递归思想):比如说Immtable.js