棧內存和堆內存在瞭解一門語言底層數據結構上,挺重要的,作了個總結數組
JS的內存空間分爲棧(stack)、堆(heap)、池(通常也會歸類爲棧中)。數據結構
其中棧存放變量,堆存放複雜對象,池存放常量,因此也叫常量池。閉包
棧是一種特殊的列表,棧內的元素只能經過列表的一端訪問,這一端稱爲棧頂。棧被稱爲是一種後入先出(LIFO,last-in-first-out)的數據結構。因爲棧具備後入先出的特色,因此任何不在棧頂的元素都沒法訪問。爲了獲得棧底的元素,必須先拿掉上面的元素。函數
堆是一種通過排序的樹形數據結構,每一個結點都有一個值。
一般咱們所說的堆的數據結構,是指二叉堆。
堆的特色是根結點的值最小(或最大),且根結點的兩個子樹也是一個堆。
因爲堆的這個特性,經常使用來實現優先隊列,堆的存取是隨意,這就如同咱們在圖書館的書架上取書,
雖然書的擺放是有順序的,可是咱們想取任意一本時沒必要像棧同樣,先取出前面全部的書,
咱們只須要關心書的名字。post
基本數據類型保存在棧內存中,由於基本數據類型佔用空間小、大小固定,經過按值來訪問,屬於被頻繁使用的數據。性能
let num1 = 1; let num2 = 1; // 閉包中的基本數據類型變量不保存在棧內存中,而是保存在堆內存中
Array,Function,Object...能夠認爲除了上文提到的基本數據類型之外,全部類型都是引用數據類型。
引用數據類型存儲在堆內存中,由於引用數據類型佔據空間大、大小不固定。
若是存儲在棧中,將會影響程序運行的性能;
引用數據類型在棧中存儲了指針,該指針指向堆中該實體的起始地址。
當解釋器尋找引用值時,會首先檢索其在棧中的地址,取得地址後從堆中得到實體this
// 基本數據類型-棧內存 let a1 = 0; // 基本數據類型-棧內存 let a2 = 'this is string'; // 基本數據類型-棧內存 let a3 = null; // 對象的指針存放在棧內存中,指針指向的對象存放在堆內存中 let b = { m: 20 }; // 數組的指針存放在棧內存中,指針指向的數組存放在堆內存中 let c = [1, 2, 3];
let a = 20; let b = a; b = 30; console.log(a); // 20
a、b 都是基本類型,它們的值是存儲在棧內存中的,a、b 分別有各自獨立的棧空間, 因此修改了 b 的值之後,a 的值並不會發生變化。
指針
let m = { a: 10, b: 20 }; let n = m; n.a = 15; console.log(m.a) // 15
m、n都是引用類型,棧內存中存放地址指向堆內存中的對象, 引用類型的複製會爲新的變量自動分配一個新的值保存在變量中, 但只是引用類型的一個地址指針而已,實際指向的是同一個對象, 因此修改 n.a 的值後,相應的 m.a 也就發生了改變。
code
在JS中,基本數據類型變量大小固定,而且操做簡單容易,因此把它們放入棧中存儲。
引用類型變量大小不固定,因此把它們分配給堆中,讓他們申請空間的時候本身肯定大小,這樣把它們分開存儲可以使得程序運行起來佔用的內存最小。
棧內存因爲它的特色,因此它的系統效率較高。
堆內存須要分配空間和地址,還要把地址存到棧中,因此效率低於棧。對象
棧內存中變量通常在它的當前執行環境結束就會被銷燬被垃圾回收制回收, 而堆內存中的變量則不會,由於不肯定其餘的地方是否是還有一些對它的引用。 堆內存中的變量只有在全部對它的引用都結束的時候纔會被回收。
閉包中的變量並不保存中棧內存中,而是保存在堆內存中。 這也就解釋了函數調用以後以後爲何閉包還能引用到函數內的變量。
function A() { let a = 1; function B() { console.log(a); } return B; } let res = A();