棧的定義
棧是計算機科學中的一種抽象數據類型,只允許在有序的線性數據集合的一端(稱為堆棧頂端,英語:top)進行加入數據(英語:push)和移除數據(英語:pop)的運算。因而按照后進先出(LIFO, Last In First Out)的原理運作。(百科全書)
棧的常用操作
棧中有兩個基本的操作
- 推入 :從棧的頂端推入一個數據,依次往下推
- 彈出 :講棧頂端的數據移除
棧的基本提點就是
- 先進后出,后進先出
- 除了頭尾的節點,每個元素都有一個先驅和一個后繼
對於棧的畫面的理解,可以想象成一個步槍彈夾添加子彈和射擊的過程
彈夾只有一個出入口進行推入和彈出
js模擬實現一個棧
<body style='width: 80%;height:80%; margin: 10% auto;'>
<ul id="stackBox" style="width:100px;border:1px solid #1fb19e;">
</ul>
<div style="width:400px; display:flex;">
<input id="funStackBox" value="執行函數">
<div style="margin-left:100px;">
<button onclick="pushStack()" type='primary'>進棧</button>
<button onclick="popStack()" type='primary'>出棧</button>
</div>
</div>
<script>
// 棧盒子
let stackBox = document.getElementById('stackBox')
// 執行函數盒子
let funStackBox = document.getElementById('funStackBox')
// 棧類
class Stack {
constructor(length) {
this.list = []
this.length = length
}
// 新增棧
addStack(val) {
if (this.length > this.list.length) {
this.list.push(val)
let beforeDom = document.getElementById(this.list.length - 1)
// 新增棧
let el = document.createElement('li')
el.id = this.list.length
el.innerText = this.list[this.list.length - 1]
stackBox.insertBefore(el, beforeDom)
}else{
console.log('棧滿了')
}
}
// 彈出棧
popStack() {
let delDom = document.getElementById(this.list.length)
stackBox.removeChild(delDom)
return this.list.pop()
}
// 棧的大小
stackLength() {
console.log('當前棧大小為'+this.list.length)
return this.list.length
}
// 棧的頂層元素
stackIsHas() {
return this.list.length?this.list[this.list.length]:console.log('沒有棧了')
}
//查看所有元素
showStack() {
return this.list.join('\n');
}
//清空隊列
clearStack() {
this.list = [];
this.showStack()
console.log('棧空了')
}
}
stackOne = new Stack(5)
/**
* @author: 周靖松
* @information: 進棧
* @Date: 2019-06-10 12:47:16
*/
function pushStack() {
stackOne.addStack(funStackBox.value)
console.log(funStackBox.value, '進棧')
stackOne.stackLength()
stackOne.stackIsHas()
}
/**
* @author: 周靖松
* @information: 出棧
* @Date: 2019-06-10 12:47:16
*/
function popStack() {
let popStack = stackOne.popStack(funStackBox.value)
console.log(popStack, '出棧')
stackOne.stackLength()
stackOne.stackIsHas()
}
</script>
</body>
效果圖如下
棧被創建的時機
上邊說了棧的基本結構和方法,那么棧被創建的時候又做了什么事情呢
首先在我們的js在解釋執行代碼的時候,最先遇到的就是全局代碼,所以在一開始的時候首先就會向棧里邊壓入一個全局執行上下文
全局上下文 只有在全局執行環境被銷毀的時候才會被彈出銷毀
全局執行上下文 只有一個 就是瀏覽器中的window對象,this默認指向這個全局對象
然后當執行一個函數的時候,會在開始先創建一個執行上下文壓入棧中,如果里邊又執行其他的函數的時候,又會創建一個新的執行上下文壓入執行棧中,直到函數執行完畢,就會把函數的上下文從執行棧中彈出
function fun3() {
console.log('3')
}
function fun2() {
fun3();
}
function fun1() {
fun2();
}
fun1();
比如說上邊這段代碼 ,他的進棧出棧順序就是一開始我們放的那兩張圖的效果
function fun1() {
console.log('1')
}
function fun2() {
console.log('2')
}
function fun3() {
console.log('3')
}
如果是這種的話 則會是最下邊有一個全局的棧,然后三個函數分別進棧出棧
棧中的執行上下文
剛才我們在上邊提到了執行上下文的概念,執行上下文是跟函數相關的,執行上下文分為兩個階段
- 創建階段
- 執行階段
首先創建階段
- 掃描變量和函數(確定變量環境)
- 確定this指向
- 確定詞法環境
簡單說一下詞法環境和變量環境的區別,我個人理解的就是說詞法環境是包含變量環境的
在js里邊原型鏈大家都不陌生 ,js在當前的對象里邊找不到所使用的屬性的話會去他的上一級去找
直到Object,再找不到就會undefined ,這里邊 當前對象的作用域就是他的變量環境,而詞法環境則是與之關聯的的執行上下文中聲明的變量
在創建階段 函數的聲明會被儲存在當前的變量環境之中,var的變量的話則會被設置成undefined
,所以我們在聲明之前就可以訪問到var聲明的變量 ,but他是一個undfined
然后就是執行階段了
這個時候已經完成了對所有變量的分配,開始執行代碼
隊列的定義
隊列是一種比較高效的數據結構,他與棧不同的是,隊列只能在隊尾插入元素,在隊首刪除元素,
隊列用生活中的事物距離的話,大家可以想想一下沙漏,先進入的沙子先出去,后進去的沙子后出去
隊列比棧高效的地方就在於,循環的時候,棧會開辟一個新的臨時棧,然后進行排序,再循環,最后在確保不打亂原有順序的情況下 排列回去
隊列則不需要這么多步驟
js模擬隊列實現
隊列常用的一些操作有
- 向隊尾添加一個元素
- 刪除隊首元素
- 顯示所有元素
- 清空隊列
- 隊列是否為空
就先拿這些常用的方法實現以下,老規矩,請大家看代碼
// 隊列類
class Queue {
constructor() {
this.dataStore = []
}
//新增
addQueue(val) {
this.dataStore.push(val);
}
//刪除隊列首的元素
delQueue() {
return this.queueIsNone()?console.log('隊列空了'):this.dataStore.shift()
}
//隊列是否為空
queueIsNone() {
return this.dataStore.length == 0
}
//查看所有元素
showQueue() {
return this.dataStore.join('\n');
}
//清空隊列
clearQueue() {
this.dataStor = [];
this.showQueue()
console.log('隊列空了')
}
}
// 隊列實例
let queueOne = new Queue()
/**
* @author: 周靖松
* @information: 進隊
* @Date: 2019-06-11 21:01:28
*/
function QueueIn (){
queueOne.addQueue(funStackBox.value)
console.log(queueOne.showQueue())
}
/**
* @author: 周靖松
* @information: 出隊
* @Date: 2019-06-11 21:01:35
*/
function QueueOut (){
queueOne.delQueue()
console.log(queueOne.showQueue())
}
/**
* @author: 周靖松
* @information: 清空隊列
* @Date: 2019-06-11 21:05:35
*/
function QueueClear(){
queueOne.clearQueue()
}
上邊的代碼 大家可以自己寫個html試一下哈,html就不寫了直接貼一下效果圖
另外雖然剛才我們再例子中是用數據去模擬的棧和隊列的實現,but 數組他其實並不是一個棧內存,他是一個堆內存
堆的定義
若是滿足以下特性,即可稱為堆:是計算機科學中的一種特別的樹狀數據結構。可以給堆中的任意節點添加新的節點(百科全書)
堆和棧的區別
在JS中,每一個數據都需要一個內存空間。內存空間又被分為兩種,棧內存(stack)與堆內存(heap)。
- 棧為自動分配的內存空間,它由系統自動釋放
- 堆是動態分配的內存,大小不定也不會自動釋放
也不是說不會自動釋放,堆在沒有引用的時候,下一次垃圾回收機制出現的時候會回收他的內存
js 的變量分為基本類型和引用類型
-
基本類型 (Undefined、Null、Boolean、Number和String)
基本類型在內存中占據空間小、大小固定 ,他們的值保存在棧(stack)空間,是按值來訪問 -
引用類型 (對象、數組、函數)
引用類型占據空間大、大小不固定, 棧內存中存放地址指向堆(heap)內存中的對象。是按引用訪問的
盜個圖舉個例子
js不允許直接訪問堆內存中的位置,因此我們不能直接操作對象的堆內存空間,所以棧中存的是一個指向堆內存的一個地址
當我們要訪問堆內存中的引用數據類型時,實際上我們首先是從棧中獲取了該對象的地址引用(或者地址指針),然后再從堆內存中取得我們需要的數據。
今天就堆棧隊列的內容就大概說到這里 下一篇博客 在繼續說一下, 有什么說的不對或者不足的地方,請大家批評指正