# [译]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数最高,其他方面也没有被拉开太大的差距。
点击查看Github最新编程语言Star统计
如果项目越来越依赖JS,意味着开发者必须使用语言生态提供的任何特性,只有越来越深入了解其内部原理,才能更好的构建出色的软件。
事实证明,很多开发者每天用着JS,但并不知道在JS运行背后发生了什么。
概述
几乎所有人都听说过V8引擎的概念,而且大多数的开发者都知道JS是单线程或者使用的是回调队列。
在这篇文章中,我们将会详细的介绍这些概念和解释JS实际的运行方式。通过详细的了解,你将能够正确的利用提供的API来无障碍的更好的开发代码。
如果你刚接触JS,这系列文章将帮助你了解JS和其他语言相比为什么如此的“奇怪”。如果你是有经验的JS开发者,希望,在日常工作中JS是如何运行,能为您提供一些新的见解。
JS引擎
JS引擎受人欢迎的一个例子就是Google的V8引擎,比如V8引擎被应用于Chrome和Node.js,这里有一个非常简单的视图:
这个引擎有两个主要的组成部分:
- 内存堆 —— 分配内存的地方
- 调用栈 —— 代码执行栈的帧(某一时刻)
运行时
被JS开发者经常使用的浏览器APIs(例如:setTimeout),然而这些API都不属于JS引擎的。所以,哪儿来的?事实证明这有些复杂。
因此,我们(浏览器)除了有引擎还有其他。那些浏览器提供的我们管它叫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);
当引擎开始执行代码,这时候栈会是空的。在这之后,会按以下步骤执行:
调用栈的任何操作可以叫它为栈帧。这也正是当异常抛出时候被构造的栈的步骤跟踪 —— 它基本上反应了当异常发生时调用栈的状态。看下面的代码:
function foo() {
throw new Error('SessionStack will help you resolve crashes :)');
}
function bar() {
foo();
}
function start() {
bar();
}
start();
当代码在Chrome执行的时候(假设代码在foo.js中),接下来的栈跟踪会被产生:
”爆栈“ —— 当你达到调用栈的最大限度将会发生。它相当的容易发生,特别是在你使用递归但没有大量测试的时候。看下面简单的代码:
function foo() {
foo();
}
foo();
当引擎开始执行代码,从调用foo函数开始。然而这是个递归调用的函数,并且在没有任何的退出条件开始调用它自己。因此,像这样的函数一次又一次添加到调用栈。看起来像:
然而像这样,函数调用使用的栈超出调用栈实际大小,浏览器将会抛出错误提示操作,就像下面:
代码运行在单线程会相对容易,因为你不需要去处理多线程环境出现的复杂情况——例如:死锁。但是运行在单线程也有很大的限制,因为JS是单个调用栈,当事件变慢的时候会怎么样?
并发和事件循环
当你有一个超级大的量需要被处理的函数被调用时,调用栈会怎么样?例如,想象一下你想处理些复杂的图像转换。你可能会问,这也是个问题?问题是,当调用栈在执行函数的时候,浏览器不能处理其他操作——是阻塞的。这意味着浏览器不能做渲染,不能执行其他的代码,仅仅被卡在这儿。如果你想要有更好的交互流程,这就是面临的问题。
不单只是这问题,一旦浏览器调用栈开始处理大量的任务,将会相当长的一段时间停止响应。大多数浏览器会给出错误提示,问您是否要终止页面。
现在看来,一点儿用户体验都没有,不是吗?
所以,我们怎么才能执行慢代码而不至于阻塞UI和使得浏览器无响应?well,方案是异步回调。
这将在第二章“JS是如果工作”有更详细的指导。“V8引擎 和 优化代码的5个小技巧”
作者:掘金-# [译]JavaScript 工作原理:引擎,运行时和调用栈概览