我对于 JavaScript 的内存模型一直都比较困惑,很想了解在操做变量的时候,JS 是如何工做的。若是你和我有一样的困惑,但愿这篇文章能给你一些启发。javascript
译文,喜欢原文的能够直接拉到底部java
当咱们声明变量、初始化变量、更改变量值的时候,到底会发生什么?JavaScript 是如何实现这些基本的功能?最重要的是,咱们如何才能理解这些基础知识?程序员
本文将覆盖如下 4 个方面:数组
从一个简单的栗子开始。首先咱们声明一个叫myNumber
的变量,赋值为 23。函数
let myNumber = 23
执行这段代码的时候,JavaScript 会...post
一般咱们会说:“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
只是追加到它的后面。从技术上讲,这是错误的。因为原始数据类型的不变性,当abc
与d
结合时,JS 会分配一个新的内存地址来保存这个值(abcd
),接着myString
指向新的地址。
图4
JS 的内存模型能够简单的理解为两个不一样的区域:调用栈和堆。
图5
栈用来保存原始数据以及函数调用,能够粗略的用下图表示。
图6
上图中,我抽象的在调用栈中显示每一个变量的值。但请记住,变量实际指向的是内存地址,那里保存着对应的值。这是理解let vs. cont
的关键。
关于堆内存。
堆保存着全部非原始类型的数据。它和栈最大的区别是,堆能够保存无序、可以动态增删的数据——对于对象和数组来讲,这是完美的存储空间。
仍是从一个简单的栗子开始。下面,咱们声明一个叫myArray
的变量,并初始化一个空数组。
let myArray = []
当 JS 引擎执行上面的代码,内存会发生以下变化:
图8
如今,咱们能够对数组作任何操做了。
myArray.push('first') myArray.push('second') myArray.push('third') myArray.pop()
图9
咱们应该优先使用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”。
他们没有明确说明为何要这样作,但我认为这样作有如下好处:
bye...