JavaScript系列:浅谈运行机制

  • 白小霁
  • 7 Minutes
  • September 2, 2017

实习快有一个月了,开心的是我司前端技术团队还是有技术分享的,每一次的分享都是针对项目而言,是有一点高逼格,动不动讲源码😲,自己也突然被安排讲,依然选了JavaScript运行机制

什么是JavaScript?

刚开始学习的时候,看了很多的书,给我们的定义是: JavaScript = ECMAScript + DOM + BOM,可是当深入的时候,我才发现很多的问题,随着ES6/7的不断推广,发现我们更多学习的是最新的ECMAScript语法而已,当发现Node的存在之后,此时的JavaScript = ECMAScript + 宿主环境API。这一刻,才发现自己的技术是多么的短浅(额,好像我也是会Java和PHP的程序员 )

那JavaScript的特性有哪些呢? 我们熟知的:单线程和非阻塞。
单线程很是比较好理解,就是同个时间只能做一件事情,那非阻塞呢?看了那么多的书籍,并没有对非阻塞进行详细的讲解。但是我们生活中会经常说的阻塞是什么呢?不知道你又没有写过一种可以让整个页面卡住的代码,造成的一种现象就是降低了代码执行的速度,这就是我们说到的阻塞。

JavaScript RunTime




运行时的JavaScript有一个Stack(栈)这是调用栈是函数调用形成的栈幀,Heap(堆)更多存的就是对复杂类型数据的引用,而Queue(队列)是一个 JavaScript 运行时包含了一个待处理的消息队列。

举例

1
2
3
4
5
6
7
8
9
10
11
function f(b){
var a = 12;
return a+b+35;
}
function g(x){
var m = 4;
return f(m*x);
}
g(21);

当g函数调用的时候,g函数先入栈,而遇到f函数,而f函数入栈,都f函数执行到return的时候,f函数出栈,随后至g函数出栈。

问题一

以上算是一个知识点的巩固,我们立即抛出一个问题:

1
2
3
4
5
6
console.log('script start');
setTimeout(function() {
console.log('setTimeout');
}, 1000);
console.log('script end');

输出的结果的顺序,上次滴滴二面最后的一个问题,先说一下这里要讲到事件循环(event-loop)
答案很明了就是

1
2
3
4
//logs
script start
script end
setTimeout

代码是由上至下解析执行的,而我们也说过JavaScript是单线程,这里有一个问题,运行到setTimeout的时候,之后仿佛出栈了,在运行到script end的console里面,那之前的回调函数是消失了吗?

这样的原因真的很少人能说上来了!本着深究的原理,我自己整理了一下思路。上文说过,JavaScript有一部分就是宿主环境给JavaScript提供了很多可用的API,而我们通常是在浏览器当中的。
所以这段代码是跑在浏览器中的,而浏览器中解析执行的JavaScript的东西是V8引擎



那我们来看看V8里面有什么呢?其实和JavaScript RunTime并没特大的区别,只是多了其他的API,具体是什么目前还说不上来。
但是我们说过,代码是在浏览器中执行的呀,我们来看看浏览器里有什么:



所以说,问题一的回答是这样的,执行到了setTimeout的时候,由于这是浏览器提供的API,交到了一个叫做WebAPI的地方帮我们托管这个代码,当时候对到1秒的时候,将回调函数放进了callback queue里面,event-loop会帮忙看调用栈是不是清空的状态,如果是,则将回调队列里的函数放进调用栈中执行。

问题二

那如下的代码怎样输出呢?

1
2
3
4
5
6
7
8
9
10
11
12
13
console.log('script start');
setTimeout(function() {
console.log('setTimeout');
}, 0);
Promise.resolve().then(function() {
console.log('promise1');
}).then(function() {
console.log('promise2');
});
console.log('script end');

不同的浏览器有不一样的输出结果,以下是由Chrome 60.03112.113版本输出结果:

1
2
3
4
5
6
// logs
script start
script end
promise1
promise2
setTimeout

这里会有一个细分的过程,任务队列是有分级的,下面有一个是 macro-task(宏任务),另一个是 micro-task(微任务),在最新标准中,它们被分别称为task与jobs。

其实还上述问题一的答案差不多,只不过中间会有一个微任务的出现,优先级是微任务在宏任务之前。

本文可能会有一些错误的地方,欢迎指出,一起交流(拖延有点严重,拖了好几天,最后20分钟码出来了☺)。参考资料