关于JS内存的一些问题栈内存堆内存
总结一些自己关于JS中内存的一些理解:他们分别是堆(heap),栈(stack),池(一般也会归类为栈中)与队列(queue)。
其中栈存放变量,堆存放复杂对象,池存放常量,所以也叫常量池。
1.栈内存(stack):
栈内存是一种特殊的列表,栈内的元素只能存列表的一端访问,这一端称为栈顶。栈被称为是一种后入先出(LIFO,last-in-first-out)的数据结构。 由于栈具有后入先出的特点,所以任何不在栈顶的元素都无法访问。 为了得到栈底的元素,必须先拿掉上面的元素。
在这里,为方便理解,通过类比乒乓球盒子来表示栈的存取方式。栈由操作系统(编译器)自动分配释放释放空间,用于存放函数的参数值、局部变量,基本数据类型。
2.堆(heap):堆是一种经过排序的树形数据结构,每个结点都有一个值。 通常我们所说的堆的数据结构,是指二叉堆。 堆的特点是根结点的值最小(或最大),且根结点的两个子树也是一个堆。 由于堆的这个特性,常用来实现优先队列,堆的存取是随意,这就如同我们在图书馆的书架上取书, 虽然书的摆放是有顺序的,但是我们想取任意一本时不必像栈一样,先取出前面所有的书, 我们只需要关心书的名字。堆一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收。
基本数据类型共有6种:
Sting
Number
Boolean
null
undefined
Symbol
基本数据类型保存在栈内存中,因为基本数据类型占用空间小、大小固定,通过按值来访问,属于被频繁使用的数据。
为了更好的搞懂基本数据类型变量与栈内存,我们结合以下例子与图解进行理解:
let Num = 1; let A = true; let B ='哈哈'; let C =null; let D= undefined; let E = symbol
Array,Function,Object...可以认为除了上文提到的基本数据类型以外,所有类型都是引用数据类型。
引用数据类型存储在堆内存中,因为引用数据类型占据空间大、大小不固定。 如果存储在栈中,将会影响程序运行的性能; 引用数据类型在栈中存储了指针,该指针指向堆中该实体的起始地址。 当解释器寻找引用值时,会首先检索其在栈中的地址,取得地址后从堆中获得实体
为了更好的搞懂变量对象与堆内存,我们结合以下例子与图解进行理解。
let F = {a:1}// 对象的指针存放在栈内存中,指针指向的对象存放在堆内存中
let g =[1,2,3]// 数组的指针存放在栈内存中,指针指向的数组存放在堆内存中
因此当我们要访问堆内存中的引用数据类型时,实际上我们首先是从变量中获取了该对象的地址指针, 然后再从堆内存中取得我们需要的数据。
3.从内存角度来看变量复制:
1.基本类型的复制
let a = 2 let b = a b = 10 console.log(a)//2
可以看出在将a复制给b后在栈内存中重新分配了内存空间给b,a、b 分别有各自独立的栈空间,所以修改b后a仍然是2,a和b之前不会相互影响。
2.引用类型的复制
let a = {c:1,d:2} let b = a d.b =10 console.log(a.d)//10
这样复制引用类型只是负责了栈内存中的指针,a,b还是会指向堆内存中的同一个对象,所以修改了b.d的值其实就是修改了a.d的值,所以对于引用类型的复 制不能这样直接使用赋值符号,需要进行深拷贝,不然会造成数据污染。
4.栈内存和堆内存的优缺点
在JS中,基本数据类型变量大小固定,并且操作简单容易,所以把它们放入栈中存储。 引用类型变量大小不固定,所以把它们分配给堆中,让他们申请空间的时候自己确定大小,这样把它们分开存储能够使得程序运行起来占用的内存最小。
栈内存由于它的特点,所以它的系统效率较高。 堆内存需要分配空间和地址,还要把地址存到栈中,所以效率低于栈。
5.栈内存和堆内存的垃圾回收
栈内存中变量一般在它的当前执行环境结束就会被销毁被垃圾回收制回收, 而堆内存中的变量则不会,因为不确定其他的地方是不是还有一些对它的引用。 堆内存中的变量只有在所有对它的引用都结束的时候才会被回收。
6.闭包与堆内存
闭包中的变量并不保存中栈内存中,而是保存在堆内存中。 这也就解释了函数调用之后之后为什么闭包还能引用到函数内的变量。
function A(){ let b = 2 function B(){ console.log(b) } return B } let d = A()//2
函数 A 返回了一个函数 B,并且函数 B 中使用了函数 A 的变量,函数 B 就被称为闭包。
函数 A 弹出调用栈后,函数 A 中的变量这时候是存储在堆上的,所以函数B依旧能引用到函数A中的变量。 现在的 JS 引擎可以通过逃逸分析辨别出哪些变量需要存储在堆上,哪些需要存储在栈上。