JavaScript内存机制之堆内存(Heap)与栈内存(Stack)详解

 更新时间:2026年05月08日 09:35:16   作者:yqcoder  
文章将JavaScript引擎中的内存分为栈和堆,详细描述了它们的特点、用途、管理方式和垃圾回收机制,通过代码实战演示了基本类型和引用类型的数据存储方式,并解释了闭包和内存泄漏的概念,总结了栈和堆的区别,提供了面试高频问题的回答方法,需要的朋友可以参考下

在 JavaScript 引擎(如 V8)中,内存主要分为两个区域:栈内存(Stack)堆内存(Heap)
它们就像公司的办公桌仓库,分工明确,协作高效。

1. 核心比喻:办公桌 vs 仓库

为了通俗易懂,我们把浏览器内存想象成一家公司:

栈(Stack) = 员工的办公桌

  • 特点:空间小,但就在手边,存取速度极快。
  • 用途:存放正在处理的临时数据(如函数调用、局部变量)。
  • 管理:老板(JS 引擎)严格管理。你离开座位(函数执行结束),桌子上的东西立刻被清空。
  • 结构:先进后出(LIFO),像叠盘子一样。

堆(Heap) = 公司的公共仓库

  • 特点:空间巨大,但距离远,存取速度相对较慢。
  • 用途:存放大件物品、复杂对象(如大数组、复杂对象、闭包)。
  • 管理:比较松散。东西扔进去就不管了,直到仓库满了,清洁工(垃圾回收器 GC)才会来清理没人用的东西。
  • 结构:无序,像一个巨大的杂物间,需要通过“地址标签”才能找到东西。

2. 栈(Stack):快速、有序、自动清理

存储内容

  • 基本数据类型Number, String, Boolean, Null, Undefined, Symbol, BigInt
  • 执行上下文:函数调用栈(Call Stack),记录当前执行到哪里了。

工作机制

  1. 分配:当声明一个变量或调用一个函数时,内存会自动在栈顶分配一块固定大小的空间。
  2. 释放:当函数执行完毕或变量超出作用域时,这块内存会自动弹出并释放。
  3. 速度:极快,因为不需要查找,直接操作栈顶指针。

注意:在 JS 中,短字符串有时也会存储在栈中(取决于引擎优化),但逻辑上我们将其视为值类型。

3. 堆(Heap):庞大、无序、手动回收

存储内容

  • 引用数据类型Object, Array, Function, Date, RegExp 等。
  • 大型数据:无论数据多大,都存放在堆中。

工作机制

  1. 分配:当创建一个对象时,引擎会在堆中开辟一块空间存放数据。
  2. 引用:栈中只存储一个指针(内存地址),指向堆中的这块数据。
  3. 释放不会自动立即释放。需要依靠浏览器的垃圾回收机制(Garbage Collection, GC) 来定期扫描,找出不再被引用的对象并清除。

4. 代码实战:数据是如何存储的?

让我们通过代码看看栈和堆是如何协作的。

场景一:基本类型(全在栈中)

let a = 10;
let b = a; // 拷贝值
b = 20;

console.log(a); // 10
console.log(b); // 20

内存图解

Stack (栈)
+-------+
| b: 20 |  <-- 修改 b,不影响 a
+-------+
| a: 10 |  <-- a 保持原值
+-------+

结论:基本类型是值拷贝。互不影响。

场景二:引用类型(栈存地址,堆存数据)

let obj1 = { name: "Lingma" };
let obj2 = obj1; // 拷贝地址(指针)
obj2.name = "Aliyun";

console.log(obj1.name); // "Aliyun" 😱 obj1 也被改变了!
console.log(obj2.name); // "Aliyun"

内存图解

Stack (栈)                  Heap (堆)
+----------+              +------------------+
| obj1     |------------->| { name: "Aliyun" } |
+----------+              +------------------+
| obj2     |-------------^  (同一个对象)
+----------+

结论:引用类型是引用拷贝(浅拷贝)。obj1obj2 指向堆中的同一个对象。修改其中一个,另一个也会变。

5. 垃圾回收:谁在打扫战场?

既然堆内存不会自动释放,那什么时候清理呢?
浏览器使用 垃圾回收机制(GC)。主流算法是 标记-清除(Mark-and-Sweep)

工作流程

  1. 标记:GC 从根节点(如 window、全局变量)出发,遍历所有能访问到的对象,打上“存活”标记。
  2. 清除:遍历堆内存,那些没有被打上标记的对象,说明已经没有任何变量引用它们了,于是被判定为“垃圾”,占用内存被释放。

内存泄漏(Memory Leak)

如果代码中存在意外的全局变量未清理的定时器循环引用,导致某些对象永远无法被 GC 标记为“可回收”,内存就会越占越多,最终导致页面卡顿甚至崩溃。

常见泄漏场景

// 1. 意外全局变量
function leak() {
  leakedVar = "I am global now"; // 忘记写 let/var/const
}

// 2. 未清除的定时器
const timer = setInterval(() => {
  console.log("I never stop");
}, 1000);
// 如果不清除 clearInterval(timer),回调函数及其引用的变量永远不会被回收

6. 总结与面试考点

特性栈(Stack)堆(Heap)
存储内容基本类型、执行上下文引用类型(对象、数组等)
大小小,固定大小大,动态分配
存取速度
管理方式自动分配与释放(LIFO)垃圾回收机制(GC)
数据结构线性,有序非线性,无序
拷贝行为值拷贝(深拷贝效果)引用拷贝(浅拷贝)

面试高频问答

Q1: 为什么基本类型赋值互不影响,而对象赋值会相互影响?

A: 因为基本类型存在栈中,赋值是复制值;对象存在堆中,栈里只存地址,赋值是复制地址,两者指向同一块堆内存。

Q2: 什么是深拷贝?如何实现?

A: 深拷贝是在堆中开辟一块新内存,将原对象的所有层级数据完整复制一份。实现方式:JSON.parse(JSON.stringify(obj))(有局限)、递归复制、或使用 structuredClone API。

Q3: 闭包会导致内存泄漏吗?

A: 不一定。闭包会阻止外部变量被回收,这是正常现象。但如果闭包长期存在且引用了巨大的无用数据,且无法被 GC 回收,才会导致泄漏。

博主寄语
理解堆和栈,不仅仅是为了应付面试。
当你遇到“修改了一个对象,另一个莫名其妙也变了”的 Bug 时,你会想起堆内存的共享特性
当你发现页面越来越卡时,你会想起堆内存的垃圾回收

记住口诀
栈小快,存基本,自动清理不费力。
堆大慢,存对象,引用拷贝要注意。
垃圾回收靠标记,内存泄漏要警惕。

以上就是JavaScript内存之堆内存(Heap)与栈内存(Stack)详解的详细内容,更多关于JavaScript堆内存(Heap)与栈内存(Stack)的资料请关注脚本之家其它相关文章!

相关文章

  • JavaScript在网页中画圆的函数arc使用方法

    JavaScript在网页中画圆的函数arc使用方法

    这篇文章主要介绍了JavaScript在网页中画圆的函数arc使用方法的相关资料,需要的朋友可以参考下
    2015-11-11
  • js 异步处理进度条

    js 异步处理进度条

    js 异步处理进度条的实现代码,需要的朋友可以参考下。
    2010-04-04
  • 微信小程序实现电子签名并导出图片

    微信小程序实现电子签名并导出图片

    这篇文章主要为大家详细介绍了微信小程序实现电子签名,并导出图片,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-05-05
  • Javascript数据结构与算法之列表详解

    Javascript数据结构与算法之列表详解

    这篇文章主要介绍了Javascript数据结构与算法之列表详解,本文讲解了列表的抽象数据类型定义、如何实现列表类等内容,需要的朋友可以参考下
    2015-03-03
  • TypeScript 泛型的使用

    TypeScript 泛型的使用

    这篇文章主要介绍了TypeScript 泛型的使用,在JavaScript中,封装一个API可以具有多种用途,因为其实弱类型语言,但是就因为是弱类型可以最终得到的结果并不是我们想要的,下面我们就来看看具体TypeScript 泛型的使用吧
    2021-12-12
  • Cropper.js进阶实现图片旋转裁剪处理功能示例

    Cropper.js进阶实现图片旋转裁剪处理功能示例

    这篇文章主要为大家介绍了Cropper.js进阶实现图片旋转裁剪功能示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-05-05
  • JavaScript asp.net 获取当前超链接中的文本

    JavaScript asp.net 获取当前超链接中的文本

    今天用到,不会。网上找到一个方法,赶快记下来。可以获取超链接的链接文本。
    2009-04-04
  • js 监控iframe URL的变化实例代码

    js 监控iframe URL的变化实例代码

    下面小编就为大家带来一篇js 监控iframe URL的变化实例代码。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-07-07
  • js实现页面a向页面b传参的方法

    js实现页面a向页面b传参的方法

    这篇文章主要为大家详细介绍了js实现页面a向页面b传参的方法,感兴趣的小伙伴们可以参考一下
    2016-05-05
  • 微信小程序之小豆瓣图书实例

    微信小程序之小豆瓣图书实例

    本篇文章主要介绍了微信小程序之小豆瓣图书实例,具有一定的参考价值,有兴趣的同学可以了解一下。
    2016-11-11

最新评论