我對於 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...