javascript 記憶體模型例項詳解
本文例項講述了javascript 記憶體模型。分享給大家供大家參考,具體如下:
我對於 JavaScript 的記憶體模型一直都比較困惑,很想了解在操作變數的時候,JS 是如何工作的。如果你和我有同樣的困惑,希望這篇文章能給你一些啟發。
譯文,喜歡原文的可以直接拉到底部
當我們宣告變數、初始化變數、更改變數值的時候,到底會發生什麼?JavaScript 是如何實現這些基本的功能?最重要的是,我們如何才能理解這些基礎知識?
本文將覆蓋以下 4 個方面:
- JavaScript 原始資料型別的變數宣告和賦值
- JavaScript 記憶體模型:呼叫棧和堆
- JavaScript 引用型別的變數宣告和賦值
- Let VS. const
JavaScript 原始資料型別的變數宣告和賦值
從一個簡單的栗子開始。首先我們宣告一個叫myNumber
的變數,賦值為 23。
let myNumber = 23
執行這段程式碼的時候,JavaScript 會...
- 為你的變數(myNumber)建立一個唯一識別符號。
- 為變數分配一個記憶體地址(執行時)。
- 在分配的地址中儲存一個值(23)。
通常我們會說:“myNumber 等於 23”,但從技術上講,myNumber
等於一個記憶體地址,那兒儲存著一個大小為 23 的值。理解這段話十分關鍵。
如果我們建立一個 newVar
的新變數,然後把 myNumber
let newVar = myNumber
因為 myNumber
實際上等於“0012CCGWH80”,那麼newVar
也等於“0012CCGWH80”,這個記憶體地址儲存的值為 23。最終實現了“newVal 等於 23”的效果。
如果我們這樣做又會發生什麼呢?
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
JavaScript 的記憶體模型:呼叫棧和堆
JS 的記憶體模型可以簡單的理解為兩個不同的區域:呼叫棧和堆。
圖5
棧用來儲存原始資料以及函式呼叫,可以粗略的用下圖表示。
圖6
上圖中,我抽象的在呼叫棧中顯示每個變數的值。但請記住,變數實際指向的是記憶體地址,那裡儲存著對應的值。這是理解let vs. cont
的關鍵。
關於堆記憶體。
堆儲存著所有非原始型別的資料。它和棧最大的區別是,堆可以儲存無序、能夠動態增刪的資料——對於物件和陣列來說,這是完美的儲存空間。
JavaScript 非原始資料型別的變數宣告和賦值
還是從一個簡單的栗子開始。下面,我們宣告一個叫myArray
的變數,並初始化一個空陣列。
let myArray = []
當 JS 引擎執行上面的程式碼,記憶體會發生如下變化:
- 為變數(myArray)建立一個唯一識別符號。
- 在棧中給變數分配一個地址a(執行時)。
- 在堆中分配一個地址b,用來儲存值 [](執行時)。
- 地址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”。
他們沒有明確說明為什麼要這樣做,但我認為這樣做有以下好處:
- 減少未來的bug。
- 使用 const 宣告變數時必須初始化,這會強迫程式設計師更加小心的處理變數作用域,帶來更好的記憶體管理和效能。
- 更好的可讀性,哪些變數是不變的,哪些會被重新賦值,一目瞭然。
bye...
原文連結
- JavaScript's Memory Model
- JavaScript 記憶體機制
更多關於JavaScript相關內容可檢視本站專題:《JavaScript常用函式技巧彙總》、《javascript面向物件入門教程》、《JavaScript錯誤與除錯技巧總結》、《JavaScript資料結構與演算法技巧總結》及《JavaScript數學運算用法總結》
希望本文所述對大家JavaScript程式設計有所幫助。