1.含義及對比
堆和棧都是運行時內存中分配的一個數據區,因此也被稱為堆區和棧區;
二者存儲的數據類型和處理速度不同;
堆(heap)用於復雜數據類型(引用類型)分配空間,例如數組對象、object對象;它是運行時動態分配內存的,因此存取速度較慢。
棧(stack)中主要存放一些基本類型的變量和對象的引用,(包含池,池存放常量),其優勢是存取速度比堆要快,並且棧內的數據可以共享,但缺點是存在棧中的數據大小與生存期必須是確定的,缺乏靈活性,
先進后出,后進先出原則,所以 push 優於 unshift
1.1 擴展: 隊列(先進先出(FIFO)的數據結構,這是事件循環(Event Loop)的基礎結構)
1.2 經典面試題
var a = 20;
var b = a; // b 為棧類型,相當於新開一塊空間然后賦值為 20,不影響原來的a
b = 30;
console.log(a)
console.log(b)
//20 30
var a = { name: '前端開發' }
var b = a;
b.name = '進階';
console.log(a)
console.log(b)
// { name: '進階' } name: '進階' }
var a = { name: '前端開發' }
var b = a;
a = null; //釋放引用
console.log(a)
console.log(b)
// null { name: '前端開發' }
var a = {n: 1};
var b = a;
a.x = a = {n: 2}; // 等價於 b.x = a = {n: 2}; 先是預編譯ab同時指向的對象增加x屬性,該對象也就是后來都沒變的b指向的對象,這里 a= {n: 2};改變了a的指向。
另外擴展下連等算法: var a=b=1,b為全局變量了 function t(){ var var a=b=1} t() b // 1 a // undefined
console.log(a.x);
console.log(b.x);
// undefined {n:2}
1.3 閉包中的變量並不保存中棧內存中,而是保存在堆內存
中,這也就解釋了函數之后之后為什么閉包還能引用到函數內的變量。
function A() { let a = 1 function B() { console.log(a) } return B }
1.4 垃圾回收算法 ( 常見內存泄漏:全局變量、定時器、閉包、dom引用 )
- 引用計數(現代瀏覽器不再使用) 循環引用 問題 function cycle() { var o1 = {}; var o2 = {}; o1.a = o2; o2.a = o1; return "cycle reference!" } cycle();
- 標記清除(常用) 從根部(在JS中就是全局對象)出發定時掃描內存中的對象,凡是能從根部到達的對象,保留。那些從根部出發無法觸及到的對象被標記為不再使用,稍后進行回收。
2.例子
棧
var a=3; var b=3;
先處理 var a=3;,首先會在棧中創建一個變量為a引用,然后查找棧中是否有3這個值,如果沒有找到,就將3存放進來,然后將a指向3。
接着處理 var b=3;,在創建為b的引用變量后,查找棧中是否有3這個值,因為此時棧中已經存在了3,便將b直接指向3。這樣,就出現了a與b同時指向3的情況。
此時,如果再令a=4,那么JavaScript解釋引擎會重新搜查棧中是否有4這個值,如果已經有了,則直接將a指向這個地址。沒有的話直接壓入並指向它。
堆
var fruit_1="apple"; var fruit_2="orange"; var fruit_3="banana"; var oArray=[fruit_1,fruit_2,fruit_3]; (此時相當於新建一個包含三個值的數組,與 fruit等不再相關) var newArray=oArray;
當創建數組時,就會在堆內存創建一個數組對象,並且在棧內存中創建一個對數組的引用
變量fruit_1、fruit_2、fruit_3為基本數據類型,它們的值直接存放在棧中
newArray、oArray為復合數據類型(引用類型),他們的引用變量存放在棧中, 指向於存放在堆中的實際對象。
比較
var str=new String('abc'); var str='abc';
第一種是用new關鍵字來新建String對象,對象會存放在堆中,每調用一次就會創建一個新的對象,而不管其字符串值是否相等及是否有必要創建新對象,從而加重了程序的負擔。並且堆的訪問速度慢。
第二種是在棧中,棧中存放值‘abc’和對值的引用;這種寫法在內存中只存在一個值,有利於節省內存空間。同時它可以在一定程度上提高程序的運行速度,因為存儲在棧中,其值可以共享,並且由於棧訪問更快;
var str1='abc'; var str2='abc'; alert(str1==str2); // true alert(str1===str2); // true
var str1=new String('abc'); var str2=new String('abc'); alert(str1==str2); // false alert(str1===str2); // false
3.細節
JavaScript堆不需要程序代碼來顯示地釋放,因為堆是由自動的垃圾回收來負責的,每種瀏覽器中的JavaScript解釋引擎有不同的自動回收方式
一個最基本的原則是:如果棧中不存在對堆中某個對象的引用,那么就認為該對象已經不再需要,在垃圾回收時就會清除該對象占用的內存空間。
因此,在不需要時應該將對對象的引用釋放掉(newArray=null;),以利於垃圾回收,這樣就可以提高程序的性能。
4. 相關文章
JavaScript是如何工作的:引擎,運行時和調用堆棧的概述!