javascript 内存模型

我对于 JavaScript 的内存模型一直都比较困惑,很想了解在操做变量的时候,JS 是如何工做的。若是你和我有一样的困惑,但愿这篇文章能给你一些启发。javascript

译文,喜欢原文的能够直接拉到底部java

当咱们声明变量、初始化变量、更改变量值的时候,到底会发生什么?JavaScript 是如何实现这些基本的功能?最重要的是,咱们如何才能理解这些基础知识?程序员

本文将覆盖如下 4 个方面:数组

  1. JavaScript 原始数据类型的变量声明和赋值
  2. JavaScript 内存模型:调用栈和堆
  3. JavaScript 引用类型的变量声明和赋值
  4. Let VS. const

JavaScript 原始数据类型的变量声明和赋值

从一个简单的栗子开始。首先咱们声明一个叫myNumber的变量,赋值为 23。函数

let myNumber = 23

执行这段代码的时候,JavaScript 会...post

  1. 为你的变量(myNumber)建立一个惟一标识符。
  2. 为变量分配一个内存地址(运行时)。
  3. 在分配的地址中存储一个值(23)。

一般咱们会说:“myNumber 等于 23”,但从技术上讲,myNumber 等于一个内存地址,那儿保存着一个大小为 23 的值。理解这段话十分关键。性能

若是咱们建立一个 newVar 的新变量,而后把 myNumber 赋值给它:code

let newVar = myNumber

由于 myNumber 实际上等于“0012CCGWH80”,那么newVar也等于“0012CCGWH80”,这个内存地址保存的值为 23。最终实现了“newVal 等于 23”的效果。对象

若是咱们这样作又会发生什么呢?blog

myNumber = myNumber + 1

显然,myNumber的值为 24,那么对于指向相同内存地址的newVar,它是否也等于 24?

答案固然是否认的!由于 JavaScript 的基本数据类型是不可变的,myNumber + 1的结果是 24,JavaScript 会分配一个新的内存地址来存储这个值,而后将myNumber指向这个新地址。

图3

再举一个例子:

let myString = 'abc'
myString = myString + 'd'

JS 新手可能认为,字符串abc已经存在于内存里,因此字母d只是追加到它的后面。从技术上讲,这是错误的。因为原始数据类型的不变性,当abcd结合时,JS 会分配一个新的内存地址来保存这个值(abcd),接着myString指向新的地址。

图4

JavaScript 的内存模型:调用栈和堆

JS 的内存模型能够简单的理解为两个不一样的区域:调用栈和堆。

图5

栈用来保存原始数据以及函数调用,能够粗略的用下图表示。

图6

上图中,我抽象的在调用栈中显示每一个变量的值。但请记住,变量实际指向的是内存地址,那里保存着对应的值。这是理解let vs. cont的关键。

关于堆内存。

堆保存着全部非原始类型的数据。它和栈最大的区别是,堆能够保存无序、可以动态增删的数据——对于对象和数组来讲,这是完美的存储空间。

JavaScript 非原始数据类型的变量声明和赋值

仍是从一个简单的栗子开始。下面,咱们声明一个叫myArray的变量,并初始化一个空数组。

let myArray = []

当 JS 引擎执行上面的代码,内存会发生以下变化:

  1. 为变量(myArray)建立一个惟一标识符。
  2. 在栈中给变量分配一个地址a(运行时)。
  3. 在堆中分配一个地址b,用来存储值 [](运行时)。
  4. 地址a所存储的值为地址b

    图7


图8

如今,咱们能够对数组作任何操做了。

myArray.push('first')
myArray.push('second')
myArray.push('third')
myArray.pop()


图9

Let vs. const

咱们应该优先使用const而不是let,除非变量会被改变。

咱们必须清楚的知道——“改变”究竟是什么意思。

值发生了变化,这是对“改变”的一种错误理解。一些 JS 程序员会写下这样的代码:

let sum = 0
sum = 1 + 2

let numbers = []
numbers.push(1)
numbers.push(2)

这段代码正确的使用let声明变量sum,由于值被改变了。然而却错误的使用let来声明变量numbers,由于他们认为给数组 push 一些数据后,数组的值被改变了。

“改变”的正确解释是——内存地址变了。let容许你改变内存地址,const则不容许。

const importantId = 489
importantId = 100 // TypeError: Assignment to constant variable

一块儿看看这到底发生了什么。

当声明importantId时,JS 引擎为其分配一个内存地址,并存储一个大小为 489 的值。切记,变量importantId等于这个内存地址。

图10

当把 100 赋值给importantId时,由于 100 是原始类型,此时会分配一个用来存储 100 的内存地址。而后 JS 尝试将新的内存地址赋值给importantId,此时就会发生错误。这是咱们想要的结果,由于咱们不想改变一个很是重要的 ID。

图11

对于新手来讲,因为不清楚“改变”的真是含义,在使用 const 声明变量可能会有些困惑,因此他们默认使用 let 来避免麻烦。

然而,这并非推荐的作法。Google 在他们的 JavaScript 风格指南中写道:“使用 const 或 let 声明全部变量。除非变量会被从新赋值,不然优先使用 const。必定不要使用 var”。

他们没有明确说明为何要这样作,但我认为这样作有如下好处:

  1. 减小将来的bug。
  2. 使用 const 声明变量时必须初始化,这会强迫程序员更加当心的处理变量做用域,带来更好的内存管理和性能。
  3. 更好的可读性,哪些变量是不变的,哪些会被从新赋值,一目了然。

bye...

原文连接

相关文章
相关标签/搜索