javascript 內(nèi)存模型實例詳解
本文實例講述了javascript 內(nèi)存模型。分享給大家供大家參考,具體如下:
我對于 JavaScript 的內(nèi)存模型一直都比較困惑,很想了解在操作變量的時候,JS 是如何工作的。如果你和我有同樣的困惑,希望這篇文章能給你一些啟發(fā)。
譯文,喜歡原文的可以直接拉到底部
當我們聲明變量、初始化變量、更改變量值的時候,到底會發(fā)生什么?JavaScript 是如何實現(xiàn)這些基本的功能?最重要的是,我們?nèi)绾尾拍芾斫膺@些基礎(chǔ)知識?
本文將覆蓋以下 4 個方面:
- JavaScript 原始數(shù)據(jù)類型的變量聲明和賦值
- JavaScript 內(nèi)存模型:調(diào)用棧和堆
- JavaScript 引用類型的變量聲明和賦值
- Let VS. const
JavaScript 原始數(shù)據(jù)類型的變量聲明和賦值
從一個簡單的栗子開始。首先我們聲明一個叫myNumber
的變量,賦值為 23。
let myNumber = 23
執(zhí)行這段代碼的時候,JavaScript 會...
- 為你的變量(myNumber)創(chuàng)建一個唯一標識符。
- 為變量分配一個內(nèi)存地址(運行時)。
- 在分配的地址中存儲一個值(23)。
通常我們會說:“myNumber 等于 23”,但從技術(shù)上講,myNumber
等于一個內(nèi)存地址,那兒保存著一個大小為 23 的值。理解這段話十分關(guān)鍵。
如果我們創(chuàng)建一個 newVar
的新變量,然后把 myNumber
賦值給它:
let newVar = myNumber
因為 myNumber
實際上等于“0012CCGWH80”,那么newVar
也等于“0012CCGWH80”,這個內(nèi)存地址保存的值為 23。最終實現(xiàn)了“newVal 等于 23”的效果。
如果我們這樣做又會發(fā)生什么呢?
myNumber = myNumber + 1
顯然,myNumber
的值為 24,那么對于指向相同內(nèi)存地址的newVar
,它是否也等于 24?
答案當然是否定的!因為 JavaScript 的基本數(shù)據(jù)類型是不可變的,myNumber + 1
的結(jié)果是 24,JavaScript 會分配一個新的內(nèi)存地址來存儲這個值,然后將myNumber
指向這個新地址。
圖3
再舉一個例子:
let myString = 'abc' myString = myString + 'd'
JS 新手可能認為,字符串abc
已經(jīng)存在于內(nèi)存里,所以字母d
只是追加到它的后面。從技術(shù)上講,這是錯誤的。由于原始數(shù)據(jù)類型的不變性,當abc
與d
結(jié)合時,JS 會分配一個新的內(nèi)存地址來保存這個值(abcd
),接著myString
指向新的地址。
圖4
JavaScript 的內(nèi)存模型:調(diào)用棧和堆
JS 的內(nèi)存模型可以簡單的理解為兩個不同的區(qū)域:調(diào)用棧和堆。
圖5
棧用來保存原始數(shù)據(jù)以及函數(shù)調(diào)用,可以粗略的用下圖表示。
圖6
上圖中,我抽象的在調(diào)用棧中顯示每個變量的值。但請記住,變量實際指向的是內(nèi)存地址,那里保存著對應(yīng)的值。這是理解let vs. cont
的關(guān)鍵。
關(guān)于堆內(nèi)存。
堆保存著所有非原始類型的數(shù)據(jù)。它和棧最大的區(qū)別是,堆可以保存無序、能夠動態(tài)增刪的數(shù)據(jù)——對于對象和數(shù)組來說,這是完美的存儲空間。
JavaScript 非原始數(shù)據(jù)類型的變量聲明和賦值
還是從一個簡單的栗子開始。下面,我們聲明一個叫myArray
的變量,并初始化一個空數(shù)組。
let myArray = []
當 JS 引擎執(zhí)行上面的代碼,內(nèi)存會發(fā)生如下變化:
- 為變量(myArray)創(chuàng)建一個唯一標識符。
- 在棧中給變量分配一個地址a(運行時)。
- 在堆中分配一個地址b,用來存儲值 [](運行時)。
- 地址a所存儲的值為地址b
圖7
圖8
現(xiàn)在,我們可以對數(shù)組做任何操作了。
myArray.push('first') myArray.push('second') myArray.push('third') myArray.pop()
圖9
Let vs. const
我們應(yīng)該優(yōu)先使用const
而不是let
,除非變量會被改變。
我們必須清楚的知道——“改變”到底是什么意思。
值發(fā)生了變化,這是對“改變”的一種錯誤理解。一些 JS 程序員會寫下這樣的代碼:
let sum = 0 sum = 1 + 2 let numbers = [] numbers.push(1) numbers.push(2)
這段代碼正確的使用let
聲明變量sum
,因為值被改變了。然而卻錯誤的使用let
來聲明變量numbers
,因為他們認為給數(shù)組 push 一些數(shù)據(jù)后,數(shù)組的值被改變了。
“改變”的正確解釋是——內(nèi)存地址變了。let
允許你改變內(nèi)存地址,const
則不允許。
const importantId = 489 importantId = 100 // TypeError: Assignment to constant variable
一起看看這到底發(fā)生了什么。
當聲明importantId
時,JS 引擎為其分配一個內(nèi)存地址,并存儲一個大小為 489 的值。切記,變量importantId
等于這個內(nèi)存地址。
圖10
當把 100 賦值給importantId
時,因為 100 是原始類型,此時會分配一個用來存儲 100 的內(nèi)存地址。然后 JS 嘗試將新的內(nèi)存地址賦值給importantId
,此時就會發(fā)生錯誤。這是我們想要的結(jié)果,因為我們不想改變一個非常重要的 ID。
圖11
對于新手來說,由于不清楚“改變”的真是含義,在使用 const 聲明變量可能會有些困惑,所以他們默認使用 let 來避免麻煩。
然而,這并不是推薦的做法。Google 在他們的 JavaScript 風(fēng)格指南中寫道:“使用 const 或 let 聲明所有變量。除非變量會被重新賦值,否則優(yōu)先使用 const。一定不要使用 var”。
他們沒有明確說明為什么要這樣做,但我認為這樣做有以下好處:
- 減少未來的bug。
- 使用 const 聲明變量時必須初始化,這會強迫程序員更加小心的處理變量作用域,帶來更好的內(nèi)存管理和性能。
- 更好的可讀性,哪些變量是不變的,哪些會被重新賦值,一目了然。
bye...
原文鏈接
更多關(guān)于JavaScript相關(guān)內(nèi)容可查看本站專題:《JavaScript常用函數(shù)技巧匯總》、《javascript面向?qū)ο笕腴T教程》、《JavaScript錯誤與調(diào)試技巧總結(jié)》、《JavaScript數(shù)據(jù)結(jié)構(gòu)與算法技巧總結(jié)》及《JavaScript數(shù)學(xué)運算用法總結(jié)》
希望本文所述對大家JavaScript程序設(shè)計有所幫助。
相關(guān)文章
javascript中的prototype屬性使用說明(函數(shù)功能擴展)
一說到prototype很多人可能第一個想到的是著名的prototype.js框架,當然我們今天說的不是它,而是Javascript中的prototype屬性,一般都被翻譯為“原型”。2010-08-08微信小程序使用ucharts在小程序中加入橫屏展示功能的全過程
這篇文章主要給大家介紹了關(guān)于微信小程序使用ucharts在小程序中加入橫屏展示功能的相關(guān)資料,文中通過實例代碼介紹的非常詳細,對大家學(xué)習(xí)或者使用微信小程序具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2022-09-09JavaScript對JSON數(shù)據(jù)進行排序和搜索
今天教給大家如何使用數(shù)組的方法來實現(xiàn)JSON數(shù)據(jù)進行排序和搜索功能,具體實例代碼大家參考下本文吧2017-07-07layer插件實現(xiàn)在彈出層中彈出一警告提示并關(guān)閉彈出層的方法
今天小編就為大家分享一篇layer插件實現(xiàn)在彈出層中彈出一警告提示并關(guān)閉彈出層的方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2019-09-09