淺析JS中的堆內存與棧內存

最近跟着組裏的大佬面試碰到這麼一個問題,javascript

Q:說說var、let、const的區別
A:balabalabalabla...
Q:const定義的值能改麼?
A:你逗我?不能吧java

不知道各位看官怎麼想?答案是部分能改,部分不能改。const定義的基本類型不能改變,可是定義的對象是能夠經過修改對象屬性等方法來改變的。如,面試

>>> const a = 1
>>> a
<<< 1
>>> a = 2
<<< VM1750:1 Uncaught TypeError: Assignment to constant variable.
    at <anonymous>:1:3
    (anonymous) @ VM1750:1
>>> const b = {}
>>> b
<<< {}
>>> b.name = 1
>>> b
<<< {name: 1}
>>> b = {}
<<< VM1785:1 Uncaught TypeError: Assignment to constant variable.
    at <anonymous>:1:4

const不是定義常量麼?爲何還能改?這就是咱們今天要說的重點~函數

js中的堆內存與棧內存

在js引擎中對變量的存儲主要有兩種位置,堆內存和棧內存學習

和java中對內存的處理相似,棧內存主要用於存儲各類基本類型的變量,包括Boolean、Number、String、Undefined、Null,**以及對象變量的指針,這時候棧內存給人的感受就像一個線性排列的空間,每一個小單元大小基本相等。指針

而堆內存主要負責像對象Object這種變量類型的存儲,以下圖
code

棧內存中的變量通常都是已知大小或者有範圍上限的,算做一種簡單存儲。而堆內存存儲的對象類型數據對於大小這方面,通常都是未知的。我的認爲,這也是爲何null做爲一個object類型的變量卻存儲在棧內存中的緣由。對象

所以當咱們定義一個const對象的時候,咱們說的常量實際上是指針,就是const對象對應的堆內存指向是不變的,可是堆內存中的數據自己的大小或者屬性是可變的。而對於const定義的基礎變量而言,這個值就至關於const對象的指針,是不可變。blog

既然知道了const在內存中的存儲,那麼const、let定義的變量不能二次定義的流程也就比較容易猜出來了,每次使用const或者let去初始化一個變量的時候,會首先遍歷當前的內存棧,看看有沒有重名變量,有的話就返回錯誤。ip

說到這裏,有一個十分很容易忽略的點,以前也是本身一直沒有注意的就是,使用new關鍵字初始化的以後是不存儲在棧內存中的。爲何呢?new你們都知道,根據構造函數生成新實例,這個時候生成的是對象,而不是基本類型。再看一個例子

var a = new String('123')
var b = String('123')
var c = '123'
console.log(a==b, a===b, b==c, b===c, a==c, a===c)  
>>> true false true true true false
console.log(typeof a)
>>> 'object'

咱們能夠看到new一個String,出來的是對象,而直接字面量賦值和工廠模式出來的都是字符串。可是根據咱們上面的分析大小相對固定可預期的即使是對象也能夠存儲在棧內存的,好比null,爲啥這個不是呢?再繼續看

var a = new String('123')
var b = new String('123')
console.log(a==b, a===b)
>>> false false

很明顯,若是a,b是存儲在棧內存中的話,二者應該是明顯相等的,就像null === null是true同樣,但結果二者並不相等,說明二者都是存儲在堆內存中的,指針指向不一致。

說到這裏,再去想想咱們常說的值類型和引用類型其實說的就是棧內存變量和堆內存變量,再想一想值傳遞和引用傳遞、深拷貝和淺拷貝,都是圍繞堆棧內存展開的,一個是處理值,一個是處理指針。

內存分配和垃圾回收

通常來講棧內存線性有序存儲,容量小,系統分配效率高。而堆內存首先要在堆內存新分配存儲區域,以後又要把指針存儲到棧內存中,效率相對就要低一些了。
垃圾回收方面,棧內存變量基本上用完就回收了,而推內存中的變量由於存在不少不肯定的引用,只有當全部調用的變量所有銷燬以後才能回收。

繼續往下思考的話,其中還有不少的東西須要去學習,今天先到這裏,後續再來補充。

話說~NaN會不會也是存儲在堆內存中的呢?你們想一想吧,歡迎你們來一塊兒討論討論~文中若有錯誤歡迎指出~

相關文章
相關標籤/搜索