一、栈1、数据结构栈是一种先进后出的数据结构。栈内存是内存中用来存放临时变量的内存块。它是一种特殊的链表,栈中的元素只能通过链表的一端访问,称为栈顶。堆栈被称为后进先出(LIFO,last-in-first-out)数据结构。由于堆栈的后进先出特性,任何不在堆栈顶部的元素都无法访问。要想得到栈底的元素,必须先移除栈顶的元素。2.数据存储为了方便大家的理解,这里我们用乒乓球盒类比来分析一下栈的访问方式。这个乒乓球的存储方式和在栈中访问数据的方式完全一样。顶楼5号乒乓球必须最后放,但可以先用。而如果我们要使用最下面的1号乒乓球,就必须把上面的4个乒乓球拿出来,这样1号乒乓球就在盒子的最上面一层。这就是栈空间的先进先出和后进先出的特点。一般来说,String、Number、Boolean、null、undefined、Symbol等基本数据类型都存放在栈内存中,因为基本数据类型占用空间小且大小固定,按值访问,使用频率高用过的数据。3.令a=20;让b=a;b=30;控制台日志(一);//此时a的值是多少,是30吗?还是20?答案是:20在这个例子中,a和b都是基本类型,它们的值存放在栈内存中。a和b有自己独立的栈空间,所以修改b的值后,a的值不会改变。4、执行和回收在JS中,基本数据类型变量的大小是固定的,操作简单易行,所以存放在栈中。一般来说,栈内存是线性有序存储的,容量小,系统分配效率高。栈内存中的变量一般在其当前执行环境结束时被垃圾回收销毁和回收。2.堆1.数据结构堆是一种有序的树状数据结构,每个节点都有一个值。通常我们所说的堆的数据结构指的就是二叉堆。堆的特点是根节点的值最小(或最大),根节点的两个子树也是一个堆。由于堆的这个特性,它经常被用来实现优先级队列。对堆的访问是随机的,就像我们在图书馆的书架上取书一样。虽然书是按顺序排列的,但是当我们想拿任何一本的时候,并不需要像一摞书一样把前面的书都拿出来,我们只需要关心书名即可。2、数据存储Array、Function、Object……除了上面说的基本数据类型外,都可以看作是引用数据类型。引用数据类型存储在堆内存中,因为引用数据类型占用空间大,大小不固定。如果存放在栈中,会影响程序运行的性能;引用数据类型在堆栈中存储一个指针,该指针指向实体在堆中的起始地址。解释器在查找引用值时,首先获取其在栈中的地址,获取到地址后,再从堆中获取实体。为了更好的理解变量对象和堆内存,我们结合下面的例子和图来进行理解。//基本数据类型-栈内存leta1=0;//基本数据类型-栈内存leta2='thisisstring';//基本数据类型-栈内存leta3=null;//对象的指针存储在栈内存中,指针指向的对象存储在堆内存中letb={m:20};//数组的指针存储在栈内存中,指针指向的数组指针存储在堆内存中letc=[1,2,3];3.举个例子来说明,当我们要访问堆内存中的引用数据类型时,其实是先从变量中获取对象的地址指针,然后再从堆内存中获取我们需要的数据。letm={a:10,b:20};letn=m;n.a=16;console.log(m.a)//此时m.a的值为多少,为10?还是16?答案是:16本例中m和n都是引用类型,栈内存中的存储地址指向堆内存中的对象。引用类型的副本会自动给新变量赋一个新值保存在变量中,但它只是引用类型的一个地址指针,实际上指向的是同一个对象,所以修改n.a的值后,相应的m.a也发生变化。4、执行和回收的引用类型变量的大小不固定,所以要分配到堆上,首先要在堆内存中分配一个新的存储区,然后将指针(地址值)存入栈内存,相对来说效率低一些。压入内存中的变量只有在指向堆内存的指针全部销毁后才会被垃圾回收,因为存在很多不确定的引用。因此,使用匿名函数,调用函数后将保存在栈内存中的指针赋值给null,也是优化性能和防止内存泄露的一种方式。
