# 一、棧(stack)
> 學習編程的時候,經常會看到stack這個詞,它的中文名字叫做"棧"。
理解這個概念,對于理解程序的運行至關重要。容易混淆的是,這個詞其實有三種含義,適用于不同的場合,必須加以區分。
<br>
<br>
## 含義一:數據結構
stack的第一種含義是一組數據的存放方式,特點為LIFO,即**后進先出**(Last in, first out)。
<br>

<br>
在這種數據結構中,數據像積木那樣一層層堆起來,后面加入的數據就放在最上層。使用的時候,最上層的數據第一個被用掉,這就叫做"后進先出"。
```
// 用數組對 棧結構 進行模擬
var stack = [];
// push就相當于放羽毛球進去,專業術語叫壓棧
stack.push('1號羽毛球');
stack.push('2號羽毛球');
stack.push('3號羽毛球');
// pop就相當于拿一個羽毛球出來, 專業術語叫彈棧
stack.pop();
```
<br>
## 含義二:代碼運行方式
stack的第二種含義是"調用棧"(call stack),表示函數或代碼像堆積木一樣存放,以實現層層調用。
<br>

1. 運行console.log(1),這個時候瀏覽器就會把console.log(1)丟進調用棧
<br>

2. 因為調用棧有東西,所以js解析器就會去執行,console.log(1)執行完畢,就會彈出調用棧,瀏覽器控制臺就會打印1
<br>
<br>
剩下的代碼如此類推,見下圖
<br>




調用像積木一樣堆起來,就叫做"調用棧"。程序運行的時候,總是先完成最上層的調用,然后將它的值返回到下一層調用,直至完成整個調用棧,返回最后的結果。
<br>
<br>
## 含義三:存儲數據的區域
stack的第三種含義是存放數據的一種內存區域。程序運行的時候,需要內存空間存放數據。一般來說,系統會劃分出兩種不同的內存空間:一種叫做stack(棧),另一種叫做heap(堆)。棧是存基本數據類型,堆存引用數據類型。
<br>
<br>
我們來看看下面的代碼:
```
var a = 20;
var b = 'abc';
var c = true;
var d = { m: 20 };
var e = d;
e.m = 30;
console.log(d.m) ???
```
<br>
<br>

1. js解析器會去找var,然后把聲明的變量都賦值undefined并且存到棧內存
<br>
<br>

2. 然后第二次讀取代碼的時候會執行聲明賦值,基本數據類型就存儲在棧內存,對象就存儲到堆內存,棧里面的d通過地址訪問堆里面關聯對象
<br>
<br>

<br>
<br>
# 二、任務隊列
JS是單線程,一次只能執行一個任務,所以所有任務都需要排隊,前一個任務結束,才會執行后一個任務。如果前一個任務耗時很長,后一個任務就不得不一直等著。
如果排隊是因為計算量大,CPU忙不過來,倒也算了,但是很多時候CPU是閑著的,比如Ajax操作從網絡讀取數據),不得不等著結果出來,再往下執行。
其實主線程完全可以不管Ajax請求,掛起處于等待中的任務,先運行排在后面的任務。等到請求返回了結果,再回過頭,把掛起的任務繼續執行下去。
于是,所有任務可以分成兩種,一種是同步任務(synchronous),另一種是異步任務(asynchronous)。同步任務指的是,在主線程上排隊執行的任務,只有前一個任務執行完畢,才能執行后一個任務;異步任務指的是,不進入主線程、而進入"任務隊列"(task queue)的任務,只有"任務隊列"通知主線程,某個異步任務可以執行了,該任務才會進入主線程執行。
<br>
具體來說,異步執行的運行機制如下。(同步執行也是如此,因為它可以被視為沒有異步任務的異步執行。)
(1)所有同步任務都在主線程上執行,形成一個執行棧(execution context stack)。
(2)主線程之外,還存在一個"任務隊列"(task queue)。只要異步任務有了運行結果,就在"任務隊列"之中放置一個事件。
(3)一旦"執行棧"中的所有同步任務執行完畢,系統就會讀取"任務隊列",看看里面有哪些事件。那些對應的異步任務,于是結束等待狀態,進入執行棧,開始執行。
(4)主線程不斷重復上面的第三步。
<br>
下圖就是主線程和任務隊列的示意圖。

只要主線程空了,就會去讀取"任務隊列",這就是JavaScript的運行機制。這個過程會不斷重復。
<br>
下面我們看一個例子:
```
console.log(1);
setTimeout(function (){
console.log(2);
}, 0);
console.log(3);
```
下面是這段代碼的執行流程圖:
<br>

<br>

<br>

延時器是外部API,異步的,所以放Web APIs里面執行,不阻塞主線程的調用棧
<br>

因為調用棧為空,這個時候```console.log(3)```進主線程的調用棧
<br>

<br>

定時器執行完畢,回調函數進任務隊列
<br>

事件循環會不斷的檢測調用棧以及隊列是否有東西,這個時候它發現調用棧為空,隊列又有任務,所以回調函數進主線程的調用棧
<br>

回調函數1執行后就執行```console.log(2)```,```console.log(2)```進調用棧
<br>

<br>

<br>
> 主線程調用棧從"任務隊列"中讀取事件并執行,這個過程是循環不斷的,所以整個的這種運行機制又稱為Event Loop(事件循環)。
<br>
<br>
<br>
```
for (var i = 1; i <= 3; i++) {
setTimeout(function () {
console.log(i);
}, 0);
}
```
<br>

<br>
```
for (var i = 1; i <= 3; i++) {
if(i == 1){
setTimeout(function () {
console.log(i);
}, 100);
}
if(i == 2){
setTimeout(function () {
console.log(i);
}, 50);
}
if(i == 3){
setTimeout(function () {
console.log(i);
}, 150);
}
}
```

## 宏任務與微任務
宏任務有:settimeout、setInterval
微任務有:Promise
事件環檢測隊列,如果有微任務,那么微任務一定比宏任務先執行,如果微任務隊列清空了,才開始執行宏任務
<br>
### 經典例題1
```
setTimeout(function () {
console.log(1);
Promise.resolve().then(() => {
console.log(3);
});
}, 0);
Promise.resolve().then(() => {
console.log(2);
setTimeout(function () {
console.log(4);
}, 0);
});
```
定時器1的回調1進宏任務隊列,promise2的回調2進微任務隊列,因為微任務比宏任務快,所以先打印2,然后執行定時器4,回調4排在回調1后面,然后回調2執行完畢,微任務清空完畢,宏任務的回調1開始打印1,然后回調3進微任務隊列,有微任務就執行微任務打印3,最后打印4。
答案:2 1 3 4
<br>
### 經典例題2
```
setTimeout(function () {
console.log(1);
Promise.resolve().then(() => {
console.log(2);
});
}, 0);
Promise.resolve().then(() => {
console.log(3);
Promise.resolve().then(() => {
console.log(4);
Promise.resolve().then(() => {
console.log(5);
});
});
setTimeout(function () {
console.log(6);
}, 0);
});
```
<br>
### 經典例題3
```
setTimeout(function () {
console.log(1);
Promise.resolve().then(() => {
console.log(2);
});
}, 0);
### 經典例題3
Promise.resolve().then(() => {
console.log(3);
Promise.resolve().then(() => {
console.log(4);
setTimeout(function () {
console.log(5);
}, 0);
});
Promise.resolve().then(() => {
console.log(6);
});
});
```
<br>
### 拓展
```
Promise.resolve().then(() => {
console.log(1);
});
// vue的nextTick是最快的微任務
this.$nextTick(() => {
console.log(2);
});
```
<br>
### 經典例題4
```
setTimeout(function () {
console.log(1);
}, 0);
this.$axios.post('/users/signin', {
account:'admin',
password:'admin'
}).then(res => {
console.log(2);
});
```
- 初級前端題
- 必會
- http協議
- 跨域
- cookie與storage
- 移動端問題
- 性能優化
- Vue全家桶
- 有哪些常用的es6語法?
- 項目
- 閉包
- JSON
- 數據類型與運算
- 數組
- DOM
- 字符串
- 要會
- async與await
- 正則
- this
- 數據加密
- 實時獲取數據
- 原生ajax
- 異步打印
- css相關
- 雜七雜八
- webpack
- 一般
- mvvm模式
- 異步請求
- XSS
- 其他dom問題
- 冷門
- 瀏覽器緩存機制
- 新
- 瀏覽器事件輪詢
- Promise
- 樹的深度優先與廣度優先
- 拷貝
- 繼承
- Vue
- 跨域
- 排序
- 瀏覽器
- 瀏覽器入門
- 瀏覽器內核知識
- 瀏覽器渲染原理
- 瀏覽器性能調優
- 自動化構建
- 字符編碼
- git
- 一些題目
- 其他
- 邏輯思維題
- 互聯網公司招聘信息如何閱讀
- bat面試