# [译]JavaScript 工作原理:引擎,运行时和调用栈概览

我心飞翔 分类:javascript

How JavaScript works: an overview of the engine, the runtime, and the call stack
write by Aug 10,2017

随着JS越来越受欢迎,团队使用JS技术栈支持了各个级别的开发 —— 前端,后端,混合应用,嵌入式设备等等。

这篇文章是深入研究JS真正是如何工作的系列中的第一篇:我们认为,通过知道JS的组成部分和他们是怎么结合在一起,你将可能写出更好的代码和应用。我们还会分享在构建SessionStack我们使用的一些经验规则。SessionStack是一款必须强大且高性能才能保持竞争力的轻量级JS应用。

如下GitHub Star统计,2017 JS在Githua活跃的仓库和总PUSH数最高,其他方面也没有被拉开太大的差距。

1.png

点击查看Github最新编程语言Star统计

如果项目越来越依赖JS,意味着开发者必须使用语言生态提供的任何特性,只有越来越深入了解其内部原理,才能更好的构建出色的软件。

事实证明,很多开发者每天用着JS,但并不知道在JS运行背后发生了什么。

概述

几乎所有人都听说过V8引擎的概念,而且大多数的开发者都知道JS是单线程或者使用的是回调队列。

在这篇文章中,我们将会详细的介绍这些概念和解释JS实际的运行方式。通过详细的了解,你将能够正确的利用提供的API来无障碍的更好的开发代码。

如果你刚接触JS,这系列文章将帮助你了解JS和其他语言相比为什么如此的“奇怪”。如果你是有经验的JS开发者,希望,在日常工作中JS是如何运行,能为您提供一些新的见解。

JS引擎

JS引擎受人欢迎的一个例子就是Google的V8引擎,比如V8引擎被应用于Chrome和Node.js,这里有一个非常简单的视图:

2.png

这个引擎有两个主要的组成部分:

  • 内存堆 —— 分配内存的地方
  • 调用栈 —— 代码执行栈的帧(某一时刻)

运行时

被JS开发者经常使用的浏览器APIs(例如:setTimeout),然而这些API都不属于JS引擎的。所以,哪儿来的?事实证明这有些复杂。

3.png
因此,我们(浏览器)除了有引擎还有其他。那些浏览器提供的我们管它叫Web APi,就像DOM,AJAX,setTimeout等等。
然后,我们有了非常令人欢迎的事件循环回调队列

调用栈

JS是一个单线程处理的语言,意味着只有单个调用栈。因此一次只能做一件事。

调用栈是记录我们的程序运行到哪儿的数据结构The Call Stack is a data structure which records basically where in the program we are.)。如果进入函数的时候,我们将这个函数放在栈顶。如果我们从函数返回,我们将栈顶弹出,这就是栈所做的。

让我们跟随下面的代码来看个例子:

function multiply(x, y) {
    return x * y;
}
function printSquare(x) {
    var s = multiply(x, x);
    console.log(s);
}
printSquare(5);
 

当引擎开始执行代码,这时候栈会是空的。在这之后,会按以下步骤执行:

4.png

调用栈的任何操作可以叫它为栈帧。这也正是当异常抛出时候被构造的栈的步骤跟踪 —— 它基本上反应了当异常发生时调用栈的状态。看下面的代码:

function foo() {
    throw new Error('SessionStack will help you resolve crashes :)');
}
function bar() {
    foo();
}
function start() {
    bar();
}
start();
 

当代码在Chrome执行的时候(假设代码在foo.js中),接下来的栈跟踪会被产生:

5.png

”爆栈“ —— 当你达到调用栈的最大限度将会发生。它相当的容易发生,特别是在你使用递归但没有大量测试的时候。看下面简单的代码:

function foo() {
    foo();
}
foo();
 

当引擎开始执行代码,从调用foo函数开始。然而这是个递归调用的函数,并且在没有任何的退出条件开始调用它自己。因此,像这样的函数一次又一次添加到调用栈。看起来像:

6.png
然而像这样,函数调用使用的栈超出调用栈实际大小,浏览器将会抛出错误提示操作,就像下面:

7.png

代码运行在单线程会相对容易,因为你不需要去处理多线程环境出现的复杂情况——例如:死锁。但是运行在单线程也有很大的限制,因为JS是单个调用栈,当事件变慢的时候会怎么样?

并发和事件循环

当你有一个超级大的量需要被处理的函数被调用时,调用栈会怎么样?例如,想象一下你想处理些复杂的图像转换。你可能会问,这也是个问题?问题是,当调用栈在执行函数的时候,浏览器不能处理其他操作——是阻塞的。这意味着浏览器不能做渲染,不能执行其他的代码,仅仅被卡在这儿。如果你想要有更好的交互流程,这就是面临的问题。

不单只是这问题,一旦浏览器调用栈开始处理大量的任务,将会相当长的一段时间停止响应。大多数浏览器会给出错误提示,问您是否要终止页面。

8.jpeg

现在看来,一点儿用户体验都没有,不是吗?

所以,我们怎么才能执行慢代码而不至于阻塞UI和使得浏览器无响应?well,方案是异步回调

这将在第二章“JS是如果工作”有更详细的指导。“V8引擎 和 优化代码的5个小技巧”

作者:掘金-# [译]JavaScript 工作原理:引擎,运行时和调用栈概览

回复

我来回复
  • 暂无回复内容