数据类型与堆栈内存模型

51 阅读2分钟

在 JavaScript 中,数据分为两大类:

  • 基本类型(Primitive Type)number, string, boolean, null, undefined, symbol, bigint
  • 引用类型(Reference Type)objectarrayfunction

这并不是语法上的随意区分,而是和 底层内存结构、性能优化、垃圾回收 深度相关。

特性基本类型引用类型
存储位置堆(栈里存指针)
大小确定(数字、布尔、指针大小固定)不确定(对象、数组可能无限扩展)
可变性不可变(string 改了就是新值)可变(属性可随时增加/删除)
拷贝方式复制值复制地址(引用)
生命周期随函数栈帧销毁GC 负责清理

为什么有这两种数据结构?

从语言设计和底层实现角度看:

1. 性能考虑

  • 栈:内存连续,大小固定,CPU 偏移量寻址 → 极快。
  • 堆:可变长存储,灵活,但分配/回收需要 GC,性能较慢。

2. 功能需求

  • 基本类型:小而固定的值,不可变,适合存在栈。
  • 引用类型:可变,对象可能无限扩展,必须存在堆。。

3. 内存安全

  • 基本类型生命周期清晰:随栈帧回收。
  • 引用类型依赖 GC(垃圾回收机制):避免内存泄漏。

栈与堆的工作方式

栈(Stack)

  • 先进后出(LIFO) ,内存连续。
  • 每次函数调用会创建一个 栈帧:保存参数、局部变量、返回地址。
  • 函数执行完,整个栈帧一次性销毁。

堆(Heap)

  • 内存分配不连续,大小不固定。
  • 存放:对象、数组、函数。
  • 生命周期不确定,由垃圾回收机制(标记清除/分代回收)管理。

为什么栈中元素大小必须固定?

  • 栈需要 连续内存,CPU 通过栈指针 + 偏移量定位。

  • 如果大小不固定,就没法计算偏移量。

  • 在 JS 引擎实现里:

    • 小整数(SMI)直接存在栈里。
    • 复杂对象统一存放一个「固定大小的指针」。

👉 所以栈的数据有规律、固定大小,而堆负责存储那些大小不确定的真实对象