浅谈JS中的堆栈
引言:我们都熟知并且常用JS变量的声明以及初始化(赋值),比如一行极其简单的代码var str = '我是字符串',那么这行代码执行的时候发生了什么呢?再比如var obj = {name: 'reslicma'}又发生了什么?他们一样吗?请看正文。
JS中变量的类型
我们先行讨论JS中变量的类型,因为JS中变量的具体存储方式是取决于这个变量的类型的。JS中的变量共有两大类:基本数据类型 和 引用数据类型,我们下文说基本型就是基本数据类型,引用型就是引用数据类型。
基本数据类型(简单数据类型)
JS中的基本型共有五种:string,number,Boolean,undefined,null。分别对应:字符串类型,数字类型,布尔类型,undefined(变量声明未初始化),null(空对象或理解为空指针)。
引用数据类型
JS中的引用型:Array,Function,Object。但是实际上就是一种:Object型,没错,就是对象,毕竟Array,Function也是对象。JS一切皆对象这句话并不为过······
栈内存和堆内存
var str = `我是字符串`,
num = 1,
bl = true,
nu = null,
un = undefined,
obj = {
name: 'reslicma'
}
复制代码
在内存中就发生了如下图这样的事情:
理解栈内存和堆内存
我们来说一下栈内存和堆内存具体的区别和联系。
栈内存就像一个线性的、规则的、大小基本固定的、有序的排列起来的一块块内存空间,就像我上图画的那样,每个单元大小固定,规则有序的排列下来,就是栈。所以,在定义一个基本型变量的时候,发生的事情如下:向栈内存申请(注意是申请)一块空间,然后把你声明的变量名和这个变量的具体的值本身压入这个申请好的小空间内。
栈内存和堆内存的优缺点
那么为什么JS要这样区分栈内存和堆内存呢?在JS中,这些基本型变量大小固定,并且操作容易简单,所以把它们放入栈中存储。引用型变量大小不固定,所以把它们分配给堆中,让他们申请空间的时候自己确定大小,这样把它们分开存储能够使得程序运行起来占用的内存最小。
栈内存和堆内存的垃圾回收
我们知道JS是有垃圾回收制的(不详细说),栈内存中基本型一般在它的当前执行环境结束就会被销毁被垃圾回收制回收,而引用类型不会,因为不确定其他的地方是不是还有一些对它的引用,所以引用型只有在所有对它的引用都结束的时候才会被回收掉。
加深理解
案例1:基本型的复制:
var num1 = 1
var num2 = num1
// 修改num1的值
num1 = 2
console.log(num2) // 还是1,不会改变
复制代码
解析具体过程:首先在栈内存中压入一个变量名为num1、值为1的一个变量。然后,第二行代码:赋值操作,先执行赋值运算符右边的式子,所以通过变量名找到了num1的值1,然后把这个值1返回并且赋值给了num2这个变量,所以栈内存中就又压入了一个变量名为num2、值为1的变量,这两个变量的值1相等但不是同一个。所以,改变num1的值就只改变了num1栈内存中的值,对num2没有任何影响。图解:
var obj1 = {name: 'reslicma'}
var obj2 = obj1
obj1.name = '我被修改了'
console.log(obj2.name) // 我被修改了
复制代码
内存图解: