摘要: 從內存角度理解 let 和 const 的意義。javascript
Fundebug經受權轉載,版權歸原做者全部。html
這是專門探索 JavaScript 及其所構建的組件的系列文章的第 21 篇。前端
若是你錯過了前面的章節,能夠在這裏找到它們:java
// 聲明一些變量並初始化它們 var a = 5; let b = "xy"; const c = true; // 分配新值 a = 6; b = b + "z"; c = false; // 類型錯誤:不可對常量賦值
做爲程序員,聲明變量、初始化變量(或不初始化變量)以及稍後爲它們分配新值是咱們天天都要作的事情。git
可是當這樣作的時候會發生什麼呢? JavaScript 如何在內部處理這些基本功能? 更重要的是,做爲程序員,理解 JavaScript 的底層細節對咱們有什麼好處。程序員
下面,我打算介紹如下內容:github
let
vs const
讓咱們從一個簡單的例子開始。下面,咱們聲明一個名爲myNumber
的變量,並用值23
初始化它。web
let myNumber = 23;
當執行此代碼時,JS 將執行:編程
myNumber
)建立惟一標識符(identifier)。23
存儲在分配的地址。雖然咱們通俗地說,「myNumber 等於 23」
,更專業地說,myNumber
等於保存值 23 的內存地址,這是一個值得理解的重要區別。小程序
若是咱們要建立一個名爲 newVar
的新變量並把 myNumber
賦值給它。
let newVar = myNumber;
由於 myNumber
在技術上實際是等於 「0012CCGWH80
」,因此 newVar
也等於 「0012CCGWH80
」,這是保存值爲23
的內存地址。通俗地說就是 newVar
如今的值爲 23
。
由於 myNumber
等於內存地址 0012CCGWH80
,因此將它賦值給 newVar
就等於將0012CCGWH80
賦值給 newVar
。
如今,若是我這樣作會發生什麼:
myNumber = myNumber + 1;
myNumber
的值確定是 24。可是newVar
的值是否也爲 24 呢?,由於它們指向相同的內存地址?
答案是否認的。因爲 JS 中的原始數據類型是不可變的,當 myNumber + 1
解析爲24
時,JS 將在內存中分配一個新地址,將24
做爲其值存儲,myNumber
將指向新地址。
這是另外一個例子:
let myString = "abc"; myString = myString + "d";
雖然一個初級 JS 程序員可能會說,字母d
只是簡單在原來存放adbc
內存地址上的值,從技術上講,這是錯的。當 abc
與 d
拼接時,由於字符串也是 JS 中的基本數據類型,不可變的,因此須要分配一個新的內存地址,abcd
存儲在這個新的內存地址中,myString
指向這個新的內存地址。
下一步是瞭解原始數據類型的內存分配位置。
JS 內存模型能夠理解爲有兩個不一樣的區域:調用堆棧(call stack)和堆(heap)。
調用堆棧是存放原始數據類型的地方(除了函數調用以外)。上一節中聲明變量後調用堆棧的粗略表示以下。
在上圖中,我抽象出了內存地址以顯示每一個變量的值。 可是,不要忘記實際上變量指向內存地址,而後保存一個值。 這將是理解 let vs. const
一節的關鍵。
堆是存儲引用類型的地方。跟調用堆棧主要的區別在於,堆能夠存儲無序的數據,這些數據能夠動態地增加,很是適合數組和對象。
讓咱們從一個簡單的例子開始。下面,咱們聲明一個名爲myArray
的變量,並用一個空數組初始化它。
let myArray = [];
當你聲明變量「myArray
」併爲其指定非原始數據類型(如「[]」)時,如下是在內存中發生的狀況:
myArray
」)從這裏,咱們能夠 push
, pop
,或對數組作任何咱們想作的。
myArray.push("first"); myArray.push("second"); myArray.push("third"); myArray.push("fourth"); myArray.pop();
代碼部署後可能存在的 BUG 無法實時知道,過後爲了解決這些 BUG,花了大量的時間進行 log 調試,這邊順便給你們推薦一個好用的 BUG 監控工具 Fundebug。
vs
const通常來講,咱們應該儘量多地使用const
,只有當咱們知道某個變量將發生改變時才使用let
。
讓咱們明確一下咱們所說的**「改變」**是什麼意思。
let sum = 0; sum = 1 + 2 + 3 + 4 + 5; let numbers = []; numbers.push(1); numbers.push(2); numbers.push(3); numbers.push(4); numbers.push(5);
這個程序員使用let
正確地聲明瞭sum
,由於他們知道值會改變。可是,這個程序員使用let
錯誤地聲明瞭數組 numbers
,由於他將把東西推入數組理解爲改變數組的值。
解釋**「改變」**的正確方法是更改內存地址
。let
容許你更改內存地址。const
不容許你更改內存地址。
const importantID = 489; importantID = 100; // 類型錯誤:賦值給常量變量
讓咱們想象一下這裏發生了什麼。
當聲明importantID
時,分配了一個內存地址,並存儲489
的值。記住,將變量importantID
看做等於內存地址。
當將100
分配給importantID
時,由於100
是一個原始數據類型,因此會分配一個新的內存地址,並將100
的值存儲這裏。
而後 JS 嘗試將新的內存地址分配給 importantID
,這就是拋出錯誤的地方,這也是咱們想要的行爲,由於咱們不想改變這個 importantID
的值。
當你將100
分配給importantID
時,其實是在嘗試分配存儲100
的新內存地址,這是不容許的,由於importantID
是用const
聲明的。
如上所述,假設的初級 JS 程序員使用let
錯誤地聲明瞭他們的數組。相反,他們應該用const
聲明它。這在一開始看起來可能使人困惑,我認可這一點也不直觀。
初學者會認爲數組只有在咱們能夠改變的狀況下才有用,const
使數組不可變,那麼爲何要使用它呢? 請記住:「改變」是指改變內存地址。讓咱們深刻探討一下爲何使用const聲明數組是徹底能夠的。
const myArray = [];
在聲明 myArray
時,將在調用堆棧上分配內存地址,該值是在堆上分配的內存地址。堆上存儲的值是實際的空數組。想象一下,它是這樣的:
若是咱們這麼作:
myArray.push(1); myArray.push(2); myArray.push(3); myArray.push(4); myArray.push(5);
執行 push
操做實際是將數字放入堆中存在的數組。而 myArray
的內存地址沒有改變。這就是爲何雖然使用const
聲明瞭 myArray,但沒有拋出任何錯誤。
myArray
仍然等於 0458AFCZX91
,它的值是另外一個內存地址22VVCX011
,它在堆上有一個數組的值。
若是咱們這樣作,就會拋出一個錯誤:
myArray = 3;
因爲 3
是一個原始數據類型,所以生成一個新的調用堆棧上的內存地址,其值爲 3
,而後咱們將嘗試將新的內存地址分配給 myArray
,因爲 myArray 是用 const 聲明的,因此這是不容許的。
另外一個會拋出錯誤的例子:
myArray = ["a"];
因爲[a]
是一個新的引用類型的數組,所以將分配調用堆棧上的一個新內存地址,並存儲堆上的一個內存地址的值,其它值爲 [a]
。而後,咱們嘗試將調用堆棧內存地址分配給 myArray
,這會拋出一個錯誤。
對於使用const
聲明的對象(如數組),因爲對象是引用類型,所以能夠添加鍵,更新值等等。
const myObj = {}; myObj["newKey"] = "someValue"; // 這不會拋出錯誤
JavaScript 是世界上排名第一的編程語言(根據 GitHub 和 Stack Overflow 的年度開發人員調查)。 掌握併成爲「JS 忍者」是咱們全部人都渴望成爲的人。
任何質量好的的 JS 課程或書籍都提倡使用let, const
來代替 var
,但他們並不必定說出緣由。 對於初學者來講,爲何某些 const 變量在「改變」其值時會拋出錯誤而其餘 const變量卻沒有。 對我來講這是有道理的,爲何這些程序員默認使用let處處避免麻煩。
可是,不建議這樣作。谷歌擁有世界上最好的一些程序員,在他們的 JavaScript 風格指南中說,使用 const 或 let 聲明全部本地變量。默認狀況下使用 const,除非須要從新分配變量,不使用 var 關鍵字(原文)。
雖然他們沒有明確說明緣由,但據我所知,有幾個緣由
const
聲明的變量必須在聲明時初始化,這迫使程序員常常在範圍方面更仔細地放置它們。這最終會致使更好的內存管理和性能。但願上面的解釋能幫助你開始明白爲何或者何時應該在代碼中使用 let 和 const 。
Fundebug專一於JavaScript、微信小程序、微信小遊戲、支付寶小程序、React Native、Node.js和Java線上應用實時BUG監控。 自從2016年雙十一正式上線,Fundebug累計處理了10億+錯誤事件,付費客戶有Google、360、金山軟件、百姓網等衆多品牌企業。歡迎你們免費試用!
轉載時請註明做者Fundebug以及本文地址: https://blog.fundebug.com/2019/04/15/javascritpt-memory-mechanism/