我們常常會聽說什么棧內存、堆內存,那么他們到底有什么區別呢,在js中又是如何區分他們的呢,今天我們來看一下。
一、棧內存和堆內存的區分
一般來說,棧內存主要用於存儲各種基本類型的變量,包括Boolean、Number、String、Undefined、Null...以及對象變量的指針,這時候棧內存給人的感覺就像一個線性排列的空間,每個小單元大小基本相等,棧內存中的變量一般都是已知大小或者有范圍上限的,算作一種簡單存儲。
而堆內存主要負責像對象Object這種變量類型的存儲,堆內存存儲的對象類型數據對於大小這方面,一般都是未知的,(所以這大概也是為什么null作為一個object類型的變量卻存儲在棧內存中的原因)。
來一張圖感受一下:
二、測試
Ⅰ.基本數據類型
/* 基本數據類型 */ var a = 1; var b = 1; console.log(a === b);//true var c = '桔子桑'; var d = '桔子桑'; console.log(c === d);//true
基本數據類型,因為都是存在棧內存中的,以上面的int為例:
var a = 1;變量 a 存在棧內存中,他的值是基本數據類型(int),自然也是在棧內存中,棧內存有沒有1?沒有那就拿出一塊內存存1,這個變量a指向這塊值為1的棧內存地址;
var b = 1;同理,變量 b 也是在棧內存中的,但是賦值的時候,發現,棧內存有一塊地址存着int型的值1,那么就直接指向這塊棧內存了;
所以最終 a === b 是 true;
Ⅱ.new 關鍵字生成的對象
/* new關鍵字 */ var a = new String('桔子桑'); var b = new String('桔子桑'); console.log(a === b);//fasle
new關鍵字生成的對象都是存在於堆內存中的,上述代碼中:
var a = new String('桔子桑');變量 a 存在於棧內存中,他的值是一個指針,這個指針指向堆內存中的一個對象!
附上一段C代碼,方便理解
int a = 20; /* 實際變量的聲明 */ int * ip; /* 指針變量的聲明 */ ip = &a; /* 在指針變量中存儲 變量a 的地址 */
所以我們應該這么理解:
普通變量的值類型是基本數據類型,指向棧內存中的一塊地址;
引用類型變量的值是指針,指向堆內存中的一塊地址。
Ⅲ.指針的賦值(引用類型)
/* 指針賦值 */ var a = new String('桔子桑'); var b = a; console.log(a === b);//true
我們看到,指針型變量 a 的值是一個指針,指向堆內存中一塊地址;
然后 又定義了一個變量 b ,他的值等於 a,a 的值是什么?a 的值是一個指針啊,那么變量 b 就等於這個指針,自然也是指向堆內存的那一塊地址咯。
圖示:
Ⅳ.const關鍵字
我們知道const關鍵字用來定義一個常量,
const
實際上保證的,並不是變量的值不得改動,而是變量指向的那個內存地址不得改動。
對於簡單類型的數據(數值、字符串、布爾值),值就保存在變量指向的那個棧內存地址,因此等同於常量。
但對於復合類型的數據(主要是對象和數組),變量指向的棧內存地址,保存的只是一個棧內存指針,const
只能保證這個指針是固定的,至於它指向的數據結構是不是可變的,就完全不能控制了。
所以,const保證的是棧內存這邊變量與其值不能發生改變,所以對於復合類型,指針指向的堆內存中存的數據結構能否改變是左右不了的。
/* const關鍵字 */ const a = 1; a = 2; const b = { name:'jack' }; b.name = 'tom';
對於前者,因為常量 a 和他的值都是在棧內存中的,不能更改,手動修改只會報錯:
而對於后者,因為常量 b 的值是一個指針,這個指針由於存在於棧內存,所以他的指向是不能更改的(堆內存地址),但是對應堆內存地址中存的數據結構(存了一個對象)是可以更改的。