10分钟理解下js中的栈(stack)和堆(heap)

5,540 阅读4分钟

1.js中为什么会有栈内存和堆内存之分?

通常与js的内存回收机制有关系,为了使程序运行时占用的内存最小。
用这个工具 http:// latentflip.com/loupe 可以帮助你了解其工作原理JavaScript的调用堆栈/事件循环/回调队列是如何相互交互的。

2.js中的变量有哪几种类型?

1.基本类型:空值(null)[特殊]、未定义(undefined)、布尔值(boolean)、数字(number)、字符串(string)、符号(symbol)[es6新增]
2.引用类型:(object) => Array, function, data, RegExp

2.1.栈内存

主要用于存放基本类型和对象变量的指针,给人的感觉就像是一个个线性排列的空间,每个小单元大小基本相等,存放的变量一般都是已知大小或者已知上限范围的,算是一种简单存储。
栈内存自动分配相对固定大小的内存空间,并由系统自动释放。

2.2.堆内存

主要用于存放引用类型诸如object这类,存储的对象类型数据对于大小在这方面都是未知的。(所以这大概也是为什么null作为一个object类型的变量却存储在栈内存中的原因)。
堆内存是动态分配内存,内存大小不一,也不会自动释放。

3.js数据测试

3.1.基本类型数据测试

var a = 123 // 是原始类型,在栈内存底部创建了一个叫做a的变量(房间),用来存放123。

这行代码在内存中做了如下事情:

var a = 123 
var b = a // 再创建一个房间b,将a的值copy一份放在叫做b的变量里面。【复制变量值】

a中的123与b中的123是完全独立的,该值只是a中123的一个副本。此后,这两个变量可以参与任何操作而不会相互影响。

var a = 123 
var b = a
a = 124 // 再创建一个房间a,里面放入值124,这样就会将之前的a的房间名字删掉,但里面放的数据不会删除。a,b两个值互不影响。


总结:因为原始值是不会发生改变的,改变的只是房间编号,并不会删除房间内的值。所以放在栈中的数据会一直放在底部。就像相机一样,我们删除照片的时候,只是把照片名删掉了,实际内存中还是会存有数据,所以我们要是想销毁数据,就只能大量往内部存照片,覆盖之前的数据就可以了

3.2.引用类型数据测试

var arr = [1,2] // 会在栈内存中命名一个arr的房间名,但是一看值是引用类型,会把值放在堆内存中,同时arr房间里面存放着[1,2]的房间名【指针名随便】

var arr = [1,2]
var arr1 = arr // 再在栈内创建一个名为arr1的房间名,把arr的值拷贝一份放入arr1的房间内,但是拷贝的是一个引用的地址,所以它们的值都是指向堆内存中的[1,2]

var arr = [1,2]
var arr1 = arr
arr.push(3) // 这个时候如果往数组内添加新的数据,就是在堆内加数据,arr1的值也会随之对应改变。

总结:从一个变量向另外一个变量复制基本类型的数据,会创建这个值的一个副本。从一个变量向另外一个变量复制引用类型的数据,复制的其实是这个值的一个指针,因此两个变量最终指向的都是同一个对象,即复制的是栈中的指针而不是堆中的对象。

1618468398(1).jpg

3.3.const关键字

1. 我们知道const关键字用来定义一个常量,const实际上保证的,并不是变量的值不得改动,而是变量指向的那个内存地址所保存的数据不得改动。
2. 对于简单类型的数据(数值、字符串、布尔值),值就保存在变量指向的那个栈内存地址,因此等同于常量。
3. 对于复合类型的数据(主要是对象和数组),变量指向的栈内存地址,保存的只是一个栈内存指针,const只能保证这个指针是固定的,至于它指向的数据结构是不是可变的,就完全不能控制了。
4. 对于简单类型,const保证的是栈内存中所保存的数据不得改动。
5. 对于复合类型,const保证的是栈内存中所保存的指针不得改动。