前言

JS的内存空间分为栈(stack)、堆(heap)、池(一般也会归类为栈中)。

其中栈存放变量,堆存放复杂对象,池存放常量,所以也叫常量池。

栈内存

访问顺序: 先进后出(栈内的元素只能通过列表的一端访问,这一端称为栈顶,另一端称为栈底)。
存储数据: 主要用于存储各种基本类型的变量,例如:BooleanNumberStringUndefinedNullSymbol以及对象变量的指针(地址值)
❗️: 闭包中的基本类型的变量不保存在栈内存中,而是保存在堆内存中。
垃圾回收: 一般在它的当前执行环境结束就会被销毁被垃圾回收制回收。
图示:

stack.jpeg

堆内存

访问顺序: 堆内存存储变量时没有什么规律可言,由于堆的这个特性,常用来实现优先队列,实现先进先出.
存储数据: Array,Function,Object…可以认为除了上文提到的基本数据类型以外,所有类型都是引用数据类型。

* 引用数据类型存储在堆内存中,因为引用数据类型占据空间大、大小不固定。 如果存储在栈中,将会影响程序运行的性能; 
* 引用数据类型在栈中存储了指针,该指针指向堆中该实体的起始地址。 当解释器寻找引用值时,会首先检索其在栈中的地址,取得地址后从堆中获得实体

垃圾回收: 一般在它的当前执行环境结束时不会被回收,因为不确定其他的地方是不是还有一些对它的引用。 堆内存中的变量只有在所有对它的引用都结束的时候才会被回收。

实例区分栈内存和堆内存

为了更好的搞懂变量对象与堆内存,我们结合代码与图解进行理解。

1
2
3
4
5
6
7
8
9
10
11
// 基本数据类型-栈内存
let a1 = 0;
// 基本数据类型-栈内存
let a2 = 'this is string';
// 基本数据类型-栈内存
let a3 = null;

// 对象的指针存放在栈内存中,指针指向的对象存放在堆内存中
let b = { m: 20 };
// 数组的指针存放在栈内存中,指针指向的数组存放在堆内存中
let c = [1, 2, 3];

exp.png

从内存角度看: 变量复制

  • 基本数据类型的复制
1
2
3
4
let a = 20;
let b = a;
b = 30;
console.log(a); // 20

解析: a、b 都是基本类型,它们的值是存储在栈内存中的,a、b 分别有各自独立的栈空间, 所以修改了 b 的值以后,a 的值并不会发生变化。

  • 引用数据类型的复制
1
2
3
4
let m = { a: 10, b: 20 };
let n = m;
n.a = 16;
console.log(m.a) // 还是16?

解析: m、n都是引用类型,栈内存中存放地址指向堆内存中的对象, 引用类型的复制会在栈中开辟出一块新的内存空间存放变量n即对象的地址指针, 实际m和n指向的是同一个对象, 所以修改 n.a 的值后,相应的 m.a 也就发生了改变。

图解如下:

copy.png


总结:大功告成✌️✌️✌️✌️✌️✌️✌️✌️✌️✌️✌️✌️✌️✌️✌️✌️✌️✌️✌️✌️

参考链接: