ChuChencheng / note

菜鸡零碎知识笔记
Creative Commons Zero v1.0 Universal
3 stars 0 forks source link

JavaScript 内存模型 #14

Open ChuChencheng opened 4 years ago

ChuChencheng commented 4 years ago

变量声明与赋值

当我们在 JavaScript 中声明一个原始值,例如

let myNumber = 123

JS 会做以下事情:

  1. 创建一个标识符 myNumber
  2. 在内存中分配一个地址,标识符指向这个地址
  3. 把赋值的 123 存入分配的地址

这时候,我们口头上会说 “myNumber 等于 123” ,但实际上, myNumber 是指向了一个地址,而地址存的值是 123

当我们重新给 myNumber 赋值时:

myNumber = 456

原来的 123 并没有消失或改变,而是重新分配了一个地址,把 myNumber 指向新的地址,并把 456 存入新地址的值。

内存模型

JavaScript 的内存模型可以理解为,存在两个区域: 调用栈

其中, 调用栈 中存着原始值的值与函数; 中存储引用类型的值。

那么引用类型的值是怎么存储的?

当我们声明一个数组:

const arr = []

发生了什么:

  1. 创建一个标识符 arr
  2. 调用栈 中分配一个地址,标识符指向这个地址
  3. 中分配一个地址,把地址存入步骤 2 中创建的地址对应的值中
  4. 将值 [] 存入 中步骤 3 分配的地址的值中

letconst

我们都知道, letconst 的区别是, let 允许开发者重新给变量赋值,而 const 反之。

实际上,这个行为限制的是能否修改标识符所指向的内存地址,例如:

const myNumber = 123
myNumber = 456 // TypeError: Assignment to constant variable.

const arr = []
arr.push(1)
arr[1] = 2
console.log(arr) // [1, 2]
arr = [3, 4] // TypeError: Assignment to constant variable.

在上述例子中可以看到,对于原始值标识符 myNumber 的重新赋值报错了,这是符合预期的。 但对于引用类型 arr ,可以修改值 [] 而不报错,但整个数组重新赋值,一样会报错。

这是因为,当我们修改 [] 的时候,我们修改的是 里面的值,而 调用栈 中, arr 标识符指向的地址没有被修改,因此不会报错;当我们重新赋值 arr 时, 调用栈 里面会重新分配地址, arr 标识符指向的地址改变了,因此报错。

参考

JavaScript’s Memory Model - Medium