JavaScript中复杂数据类型采用“栈存地址、堆存实体”机制:栈中存储指向堆内存的指针,赋值与传参均为地址复制,===比较地址是否相同,null是空指针而非对象。

复杂数据类型(如对象、数组、函数、日期、正则等)在 JavaScript 中的内存分配遵循“栈存地址、堆存实体”的规则,这是理解引用行为、赋值逻辑和内存泄漏的关键。
栈中只存指针,堆中才存真实数据
声明一个复杂类型变量时,JS 引擎会在栈内存中分配一小块固定空间,里面不放数据本身,而是存放一个指向堆内存的地址(类似门牌号)。真正的数据——比如 {name: "张三", age: 25} 或 [1, 2, {x: 1}]——会被完整地存在堆内存里。
- 栈空间由操作系统自动管理,速度快、生命周期短(比如函数执行完就释放)
- 堆空间动态分配,容量大,由垃圾回收器(GC)按需清理,适合存大小不确定、结构嵌套深的数据
- 你用
typeof查复杂类型总是返回"object"(包括null和数组),正是因为它们都通过对象机制管理,底层依赖堆+指针模型
赋值和传参本质都是复制地址
当你写 let b = a(其中 a 是对象或数组),JS 并没有把整个对象拷贝一份,只是把栈里 a 存的那个地址复制给了 b。此时 a 和 b 指向堆中的同一块区域。
- 修改
b.name = "李四",a.name也会变成"李四"——因为改的是堆里的同一个对象 - 函数传参同理:形参接收到的不是副本,而是实参栈中地址的拷贝,所以函数内修改对象属性会反映到外部
- 但重新赋值(如
b = {}或b = null)只改变b自己栈里的地址,不影响a
如何判断两个变量是否引用同一对象
用严格相等 === 可以直接比较栈中的地址值:
-
{} === {}是false(两个新对象,堆地址不同) -
let a = {}; let b = a; a === b是true(地址相同) -
Object.is(a, b)行为与===基本一致,对NaN和-0/+0更严谨,但不改变引用判断逻辑
常见误区提醒
不要误以为“变量等于对象”,变量其实等于一个地址;也不要认为 null 是对象——它只是历史遗留的 typeof 错误,语义上表示“空指针”,栈中存的就是一个特殊标记值,不指向任何堆地址。
数组、函数、new Date() 等所有引用类型,都共享这套内存模型。理解这点,浅拷贝、深拷贝、闭包变量捕获、循环引用 GC 等问题就自然清晰了。











