栈(stack)
JavaScript中Array数组模拟栈:
var arr = [1, 2, 3, 4, 5]; arr.push(6); // 存入数据 arr -> [1, 2, 3, 4, 5, 6] arr.pop(); // 取出数据 arr -> [1, 2, 3, 4, 5]
堆(heap)
堆的特点是"无序"的key-value
"键值对"存储方式。
"堆的存取方式跟顺序没有关系,不局限出入口"。
队列 (queue)
JavaScript中Array数组模拟队列:
var arr = [1, 2, 3, 4, 5]; // 队尾in arr.push(6); // 存入 arr -> [1, 2, 3, 4, 5, 6] // 队头out arr.shift(); // 取出 arr -> [2, 3, 4, 5, 6]
栈、堆、队列在JavaScript中的应用
1. 代码运行方式(栈应用/函数调用栈)
JavaScript中函数的执行过程,其实就是一个入栈出栈的过程:
- 当脚本要调用一个函数时,JS解析器把该函数推入栈中(push)并执行
- 当函数运行结束后,JS解析器将它从堆栈中推出(pop)
2. 内存存储(栈、堆)
JS中的基础数据类型,这些值都有固定的大小,往往都保存在栈内存中,因此基础数据类型都是按值访问
JS的引用数据类型,比如数组Array,它们值的大小是不固定的。引用数据类型的值是保存在堆内存中的对象。JS不允许直接访问堆内存中的位置,因此我们不能直接操作对象的堆内存空间。在操作对象时,实际上是在操作对象的引用而不是实际的对象。因此,引用类型的值都是按引用访问的。这里的引用,我们可以粗浅地理解为保存在栈内存中的一个地址,该地址与堆内存的实际值相关联。
3. 事件轮询(队列)
JavaScript中事件轮询(Event Loop)的执行机制,就是采用队列的存取方式,因事件轮询(Event Loop)也是JS基础中的一个比较难理解的知识点。
深浅拷贝
将一个变量的值赋值给另一个变量,相当于在栈内存中创建了一个新的内存空间,然后从栈中复制值,存储到这个新空间中。对于基本类型,栈中存储的就是它自身的值,所以新内存空间存储的也是一个值。直接改变新变量的值,不会影响到旧变量的值,因为他们值存储的内存空间不同。
// 基本类型复制变量 var a = 10; var b = a; b = 20; a // 10 b // 20
而对于引用类型来说,同样是复制栈中存储的值。但是栈存储的只是其引用地址,其具体的值存储在堆中。变量复制仅复制栈中存储的值,不会复制堆中存储的值,所以新变量在栈中的值是一个地址指针。
// 引用类型复制变量 var a = { age: 27 }; var b = a; b.age = 29; a.age == b.age; // 29
可见,变量复制赋值,都属于栈存储拷贝,因此深浅拷贝可以这样区分:
- "浅拷贝:栈存储拷贝"
- "深拷贝:栈堆存储拷贝"
深拷贝会同时开辟新的栈内存,堆内存空间。