# 一、栈(stack)
> 学习编程的时候,经常会看到stack这个词,它的中文名字叫做"栈"。
理解这个概念,对于理解程序的运行至关重要。容易混淆的是,这个词其实有三种含义,适用于不同的场合,必须加以区分。
<br>
<br>
## 含义一:数据结构
stack的第一种含义是一组数据的存放方式,特点为LIFO,即**后进先出**(Last in, first out)。
<br>
![](https://box.kancloud.cn/0bfd496a679a94344736cc29fb3681e8_385x260.png)
<br>
在这种数据结构中,数据像积木那样一层层堆起来,后面加入的数据就放在最上层。使用的时候,最上层的数据第一个被用掉,这就叫做"后进先出"。
```
// 用数组对 栈结构 进行模拟
var stack = [];
// push就相当于放羽毛球进去,专业术语叫压栈
stack.push('1号羽毛球');
stack.push('2号羽毛球');
stack.push('3号羽毛球');
// pop就相当于拿一个羽毛球出来, 专业术语叫弹栈
stack.pop();
```
<br>
## 含义二:代码运行方式
stack的第二种含义是"调用栈"(call stack),表示函数或代码像堆积木一样存放,以实现层层调用。
<br>
![](https://box.kancloud.cn/db72d02c411342045393de826dd5875f_562x303.png)
1. 运行console.log(1),这个时候浏览器就会把console.log(1)丢进调用栈
<br>
![](https://box.kancloud.cn/ab313f90b2f472b07a5d14d0c28278ac_857x376.png)
2. 因为调用栈有东西,所以js解析器就会去执行,console.log(1)执行完毕,就会弹出调用栈,浏览器控制台就会打印1
<br>
<br>
剩下的代码如此类推,见下图
<br>
![](https://box.kancloud.cn/dd344f564d69467357bf3946ea3fb9be_932x387.png)
![](https://box.kancloud.cn/209a446976c9f2047e6447b506745669_856x376.png)
![](https://box.kancloud.cn/9c5b207c626aa1537a4838df66357288_893x370.png)
![](https://box.kancloud.cn/4a0c25429d8467b8779c3fdc940965d5_859x384.png)
调用像积木一样堆起来,就叫做"调用栈"。程序运行的时候,总是先完成最上层的调用,然后将它的值返回到下一层调用,直至完成整个调用栈,返回最后的结果。
<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>
![](https://box.kancloud.cn/aa987499988edaf05b86cf051f20cff7_722x303.png)
1. js解析器会去找var,然后把声明的变量都赋值undefined并且存到栈内存
<br>
<br>
![](https://box.kancloud.cn/4dd90c627a6503c38fac54d1449c4967_728x317.png)
2. 然后第二次读取代码的时候会执行声明赋值,基本数据类型就存储在栈内存,对象就存储到堆内存,栈里面的d通过地址访问堆里面关联对象
<br>
<br>
![](https://box.kancloud.cn/1220e76405bdaf477bea0f8e0af78cb7_736x337.png)
<br>
<br>
# 二、任务队列
JS是单线程,一次只能执行一个任务,所以所有任务都需要排队,前一个任务结束,才会执行后一个任务。如果前一个任务耗时很长,后一个任务就不得不一直等着。
如果排队是因为计算量大,CPU忙不过来,倒也算了,但是很多时候CPU是闲着的,比如Ajax操作从网络读取数据),不得不等着结果出来,再往下执行。
其实主线程完全可以不管Ajax请求,挂起处于等待中的任务,先运行排在后面的任务。等到请求返回了结果,再回过头,把挂起的任务继续执行下去。
于是,所有任务可以分成两种,一种是同步任务(synchronous),另一种是异步任务(asynchronous)。同步任务指的是,在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务;异步任务指的是,不进入主线程、而进入"任务队列"(task queue)的任务,只有"任务队列"通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。
<br>
具体来说,异步执行的运行机制如下。(同步执行也是如此,因为它可以被视为没有异步任务的异步执行。)
(1)所有同步任务都在主线程上执行,形成一个执行栈(execution context stack)。
(2)主线程之外,还存在一个"任务队列"(task queue)。只要异步任务有了运行结果,就在"任务队列"之中放置一个事件。
(3)一旦"执行栈"中的所有同步任务执行完毕,系统就会读取"任务队列",看看里面有哪些事件。那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行。
(4)主线程不断重复上面的第三步。
<br>
下图就是主线程和任务队列的示意图。
![](https://box.kancloud.cn/7b324ea98928a6097a95deb458e4e05b_581x420.png)
只要主线程空了,就会去读取"任务队列",这就是JavaScript的运行机制。这个过程会不断重复。
<br>
下面我们看一个例子:
```
console.log(1);
setTimeout(function (){
console.log(2);
}, 0);
console.log(3);
```
下面是这段代码的执行流程图:
<br>
![](https://box.kancloud.cn/ce1b57f2d3e1f9cb12fb8e7bee140d16_1288x585.png)
<br>
![](https://box.kancloud.cn/8ea1af7f9a6a1d2719a0d9c7767276bc_1275x593.png)
<br>
![](https://box.kancloud.cn/071e808f743bec8fcbaa69408724394b_1312x574.png)
延时器是外部API,异步的,所以放Web APIs里面执行,不阻塞主线程的调用栈
<br>
![](https://box.kancloud.cn/aa3407ac9ee49165079d17799b1f4d04_1274x577.png)
因为调用栈为空,这个时候```console.log(3)```进主线程的调用栈
<br>
![](https://box.kancloud.cn/cc7e6732d7fb2f3d76263b53c6edc832_1282x563.png)
<br>
![](https://box.kancloud.cn/5b203e33463d98fd51d64729e0e2d5c2_1286x577.png)
定时器执行完毕,回调函数进任务队列
<br>
![](https://box.kancloud.cn/5e8173916ed272f5ba53ed2e28871c92_1290x576.png)
事件循环会不断的检测调用栈以及队列是否有东西,这个时候它发现调用栈为空,队列又有任务,所以回调函数进主线程的调用栈
<br>
![](https://box.kancloud.cn/13550c9943beb5b4ed51271c126c3276_1344x570.png)
回调函数1执行后就执行```console.log(2)```,```console.log(2)```进调用栈
<br>
![](https://box.kancloud.cn/102aa1ded5888aeeeca2cf257ddc9c79_1346x553.png)
<br>
![](https://box.kancloud.cn/5de37b21ed9904b3266862c45c75c7c5_1320x556.png)
<br>
> 主线程调用栈从"任务队列"中读取事件并执行,这个过程是循环不断的,所以整个的这种运行机制又称为Event Loop(事件循环)。
<br>
<br>
<br>
```
for (var i = 1; i <= 3; i++) {
setTimeout(function () {
console.log(i);
}, 0);
}
```
<br>
![](https://box.kancloud.cn/6efa07d55bd9a0ecaebe8b73bcd008bb_1392x562.png)
<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);
}
}
```
![](https://box.kancloud.cn/c8a841d07267426a04ab4fbe26a20851_1379x546.png)
## 宏任务与微任务
宏任务有: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面试