原文链接:an overview of the engine, the runtime, and the call stack
Foreword - 前言
随着js越来越流行,团队利用其在更多领域的堆栈,前端,后端,混合app,嵌入设备等。 这篇文章旨在深入了解js以及它的工作原理。我们都知道只有了解更多的js基础才可以写出更好的代码和app。
Overview - 概览
几乎所有人都听过V8引擎的概念,也知道js是一个单线程活使用回调队列。 在这篇文章中我们会详细讲解这些概念和解释js工作原理。知道这些你可以利用js提供的api,写出更好的代码,更流畅的app应用。 如果你刚接触js,这篇博文会帮助你理解js和其他语言相比下的与众不同。 如果你是一个经验丰富的js开发者,在你每天的工作的时候,在js如何运行上给你一些新的体验。
The JavaScript Engine - js引擎
谷歌v8引擎是一个js引擎的典型案例。Chrome和node都是用了v8引擎。下面是一个简单的例图:
v8引擎由两部分组成:
- Memory Heap - 内存堆:内存分配
- Call Stack - 调用栈:调用栈框架代码执行
The Runtime - 运行
浏览器提供的api几乎被所有js开发者使用(eg:'setTimerout')。然而这些api并不是v8引擎提供的。 因此他们来自哪里呢? 事实证明现实是有一点复杂的。
因此,我们拥有引擎,但实际上还有更多的东西。有浏览器提供的web api,例如 dom,ajax,setTimeout等等。 而且,我们还有超级流行的 event loop 和 回调队列。
The Call Stack - 调用栈
js是一个单线程的程序语言,意味着他只有一个调用栈。所以一次只可以做一件事情。 The Call Stack is a data structure which records basically where in the program we are. 如果进入一个函数,我们把它放入调用栈的顶端。如果我从一个函数返回,我们把它从调用栈顶端移除,这就是调用栈做的所有事。 看下面demo:
function multiply(x, y) {
return x * y;
}
function printSquare(x) {
var s = multiply(x, x);
console.log(s);
}
printSquare(5);
复制代码
当引擎开始执行代码,调用栈是空的。然后,接下来的步骤如图:
调用栈中的每一个表被称为一个 栈框架(Stack Frame) 当异常被抛出的时候,可以清楚的知道堆栈追踪是如何被构成的。异常发生时堆栈的状态。看一下下面的代码:
function foo() {
throw new Error('SessionStack will help you resolve crashes :)');
}
function bar() {
foo();
}
function start() {
bar();
}
start();
复制代码
如果这发生在 Chrome 里(假设这段代码实在一个名为 foo.js 的文件中),那么将会生成以下的堆栈追踪
堆栈溢出(Blowing the stack) - 当你达到调用栈最大的大小的时候就会发生。而且很容易出现,特别是当你在没有完全的测试你的代码的情况下使用回调。看下面的简单的代码:
function(){
foo();
}
foo();
复制代码
当引擎开始解析这段代码的时候,就会开始调用foo函数,这个函数是一段从自己开始没有任何终止条件的递归函数。因此在每一步的执行中,同样的函数被一遍一遍放在调用栈中,这看起来就像这样:
然后在某一时刻,当函数调用栈的数量超过实际调用栈的大小的时候,浏览器会通过抛出异常来采取行动。看起来就像这样: 在单线程中很容易运行代码因为你不用去处理多线程环境下的复杂场景-例如:死锁。 但是在单线程中运行很容易被限制。因为js只有一个单线程,当运行变慢时发生了什么?Concurrency & the event loop - 并发&事件循环
当你有函数调用堆栈需要花费大量时间处理的时候,发生了什么?例如,你想要在浏览器中使用js去做一些复杂的图片处理的时候。 你可能会问-这怎么是一个问题?问题是当调用栈有函数要执行。浏览器实际上不可以做别的事情--它被锁住了。这意味着浏览器不可以被渲染,也不可以执行其他的代码,它仅仅是卡住了。而且这会对于你想要完美的ui app中造成问题。 而且不仅仅是这一个问题。一旦你的浏览器在调用栈中开始执行这么多任务,他可能会在很长时间内停止响应。绝大多数浏览器会采取提出一个错误,询问你是否需要关闭web页面。 现在,这难道不是一个很糟糕的用户体验? 所以,我们在执行大量代码的时候如何不会阻塞ui和浏览器流畅?就决方案就是 异步回调(asynchronous callbacks)。
会在第二章中有更详细的解释:Inside the V8 engine + 5 tips on how to write optimized code