Skip to content

JS 栈内存和堆内存

简单来说,JS 引擎将内存分为代码区和数据区,而数据区分为 Stack(栈内存)和 Heap(堆内存)。

基本类型的数据直接存入 Stack 里,复杂类型的数据是把 Heap 地址存入 Stack 里,这个地址就是对数据的引用。

记录一下几个关于内存的问题:

javascript
var a = 1
var b = a
b = 2
// a = ?

不废话。。。a 的值肯定为 1。

javascript
var a = { name: 'a' }
var b = a
b = { name: 'b' }
// a.name = ?

因为 a 的值为一个对象(复杂类型),所以 a 其实是把这个对象的 Heap 地址存在 Stack 里。

var b = a 是复制了这个 Heap 地址给 b。

b = { name: 'b' } 是把一个新对象的 Heap 地址赋值给 b,覆盖掉原来的 Heap 地址。

所以 a 没有变化,a.name 的值为 'a'

javascript
var a = { name: 'a' }
var b = a
b.name = 'b'
// a.name = ?

前两句同上。

此时 b.name = 'b' 其实是把 Heap 地址引用的对象中的 name 变成了 'b'

因为 a 和 b 的 Heap 地址引用的是同一个内存区域(即同一个对象),所以 a.name 的值也变成了 'b'

javascript
var a = { name: 'a' }
var b = a
b = null
// a.name = ?

其实与第二题同理,b = null 是把 null 赋值给 b,覆盖掉原来的 Heap 地址。

所以 a 没有变化,a.name 的值还是为 'a'

javascript
var a = { self: a }
// a === a.self ?

首先变量提升会声明一个 a,此时 a 并没有被赋值,a 为 undefined

然后在 Heap 中存 { self: a } 数据,此时 a 依然为 undefined

最后再把该数据的 Heap 地址赋值给 a 存入 Stack 里。

所以实际上 a 的值为 { self: undefined }a.selfundefined。结果为 false

如果要实现 a === a.self ,要先存一个空对象,再给空对象添加属性。

var a = {}
a.self = a
a === a.self  // true
javascript
var a = { name: 'a' }
var b = a
a.x = a = { name: 'b' }
// a.x = ?
// b.x = ?

首先写出 a.x = a = { name: 'b' } 这样的代码很智障。。。

然后运行到这一行的时候,a 和 b 在 Stack 中存入的 Heap 地址已经确定了,简单的假设这个地址为 ADDR200,写成伪代码 ADDR200.x = ADDR200 = { name: 'b' }

先计算 a = { name: 'b' } 就是覆盖之前的 Heap 地址,再简单的假设这个地址为 ADDR300,写成伪代码 ADDR200.x = ADDR300

再计算 a.x = a 就是给 Heap 地址为 ADDR200 引用的对象添加属性 x,值为 Heap 地址 ADDR300

所以最后 a 在 Stack 中存的 Heap 地址为 ADDR300,b 在 Stack 中存的 Heap 地址为 ADDR200

javascript
a  // { name: 'b' }
b  // { name: 'a', x: { name: 'b' } }
a.x  // undefined
b.x  // { name: 'b' }