介紹 棧(stack) 棧stack為自動分配的記憶體空間,它由系統自動釋放 堆(heap) 堆heap是動態分配的記憶體,大小不定也不會自動釋放 動態演示地址 http://latentflip.com/loupe 數據類型 基本類型:String、Number、Boolean、Null、Undefi ...
介紹
棧(stack) 棧stack為自動分配的記憶體空間,它由系統自動釋放
堆(heap) 堆heap是動態分配的記憶體,大小不定也不會自動釋放
動態演示地址
數據類型
- 基本類型:String、Number、Boolean、Null、Undefined、symbol(ES6)
- 基本類型在記憶體中占據空間小、大小固定 ,他們的值保存在棧(stack)空間,是按值來訪問
- 引用類型:Object、Array、Date、Function、Error、RegExp、Math、Number、String、Boolean、Globle。
- 引用類型占據空間大、大小不固定, 棧記憶體中存放地址指向堆(heap)記憶體中的對象。是按引用訪問的
堆棧
- 堆:用於引用數據類型(數組,對象,函數)分配空間,從棧記憶體指向堆記憶體的數據結構
- 引用數據類型Object(Array,Date,RegExp,Function...)
- 堆記憶體釋放
- 讓所有引用堆記憶體空間地址的變數賦值為null即可,當堆記憶體沒有被任何的變數或者其他東西引用時,就會在瀏覽器執行垃圾回收的時候,被銷毀掉。
var obj = {'name': 'along'}
堆記憶體 |
|
{'name': 'along'} |
指針 |
obj2 = obj
堆記憶體 |
|
{'name': 'along'} |
指針 |
{'name': 'along'} |
指針 |
結論:你會發現,如果是引用類型複製的話,雖然會有兩個指針,但是指向都是同一個對象,這就會造成如果改變obj2值,也會造成obj值的改變。
如下圖所示:棧記憶體中存放的只是該對象的訪問地址, 在堆記憶體中為這個值分配空間 。 由於這種值的大小不固定,因此不能把它們保存到棧記憶體中。但記憶體地址大小的固定的,因此可以將記憶體地址保存在棧記憶體中。 這樣,當查詢引用類型的變數時, 先從棧中讀取記憶體地址, 然後再通過地址找到堆中的值。
- 棧(先進後出/後進先出的數據結構)
- 棧基本五種數據類型: String、Number、Boolean、Null、Undefined、symbol(ES6)
- 棧記憶體釋放
- 全局作用域會在頁面關閉或者刷新的時候釋放。
- 私有作用域一般情況下,當函數執行完成,所形成的私有作用域(棧記憶體)都會自動釋放掉,但是也有特殊的情況。(閉包)
- 結構特點
- 示例
var a = 2;
棧記憶體 |
|
a |
2 |
b = a
棧記憶體 |
|
a |
2 |
b |
2 |
結論:棧記憶體雖然複製了a,但是創建了新的隊列。修改b的值的同時也不會改變a的值。
代碼實現
使用數組實現棧的結構
function Stack(){ let items = []; this.size = function(){ return items.length } this.push = function(item){ items.push(item) } this.pop = function(){ let item = items.pop() console.log('pop',item) return item } this.top = function(){ let length = items.length; console.log('top',items[length-1]) } this.show = function(){ console.log(items) } this.clear = function(){ items.length = 0 } this.isEmpty = function(){ console.log(items.length === 0) return items.length === 0 } } let newStack = new Stack(); newStack.push(12); newStack.show() newStack.top() newStack.pop(); newStack.isEmpty(); newStack.show() newStack.push(1); newStack.push(5); newStack.isEmpty(); newStack.top() newStack.show() newStack.clear(); newStack.show()
棧記憶體溢出
調用堆棧會一直增長,直到達到限制,瀏覽器硬編碼堆棧大小或記憶體耗盡。所以在用遞歸函數時要給一個終止條件。
var count = 1; function fn() { count++; fn() } fn() //Maximum call stack size exceeded
History API與瀏覽器堆棧管理
pushState,它會向瀏覽器的歷史堆棧壓入一個url為設定值的記錄,並改變歷史堆棧的當前指針至棧頂。
history.pushState(stateObject, title, url)
stateObject:state是一個由 pushState()方法創建的、與歷史紀錄相關的JS對象。當用戶定向到一個新的狀態時,會觸發popstate事件。事件的state屬性包含了歷史紀錄的state對象。
title:目前大多瀏覽器都忽視,傳遞空即可
url:新曆史記錄的url地址
replaceState:與pushState參數相同,含義也相同。唯一的區別在於replaceState是替換瀏覽器歷史堆棧的當前歷史記錄為設定的url。需要註意的是,replaceState不會改動瀏覽器歷史堆棧的當前指針。
onpopstate:該事件是window的屬性。該事件會在調用瀏覽器的前進、後退以及執行history.forward、history.back、和history.go觸發,因為這些操作有一個共性,即修改了歷史堆棧的當前指針。在不改變document的前提下,一旦當前指針改變則會觸發onpopstate事件。
創建一個新的瀏覽器記錄狀態
const state = { 'page_id': 1, 'user_id': 5 } const title = '' const url = 'hello-world.html' history.pushState(state, title, url)
總結:
瀏覽器針對每個頁面維護一個History棧。執行pushState函數可壓入設定的url至棧頂,同時修改當前指針
當執行back操作時,history棧大小並不會改變(history.length不變),僅僅移動當前指針的位置
詳細查看:https://www.yuque.com/along-n3gko/ezt5z9/gle5df