12

Javascript 的工作原理:引擎、运行时和调用堆栈概述

 3 years ago
source link: https://xie.infoq.cn/article/cee621236dcfb765b39b40985?
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

随着 JavaScript 越来越流行,使其应用的场景越来越多,不仅限于前端,可以是后端、混合应用程序、嵌入式设备等等,于是就有了大前端的叫法。本文开始带大家一起回顾总结 JavaScript 的构建块以及它们是如何协同工作,理解其原理,将有助于编写更优的代码。

大部份人都听说过 V8 引擎,都知道 JavaScript 是单线程的,或者它使用的是回调队列。下面将围绕这些概念展开,并解释 JavaScript 的实际运行方式。通过了解这些细节,能够编写更好的、无阻塞的应用程序,并正确利用所提供的 API。

JavaScript 引擎

JavaScript 引擎中最流行的莫过于 Google 开发的 V8 引擎,可以用于 Chrome 和 Node.js。下面是它的架构图:

auto-orient,1

引擎由两个主要部份组成:

  • 内存堆:内存分配发生的地方

  • 调用栈:代码执行时的堆栈帧所在的地方

Runtime

浏览器中的 API 几乎大部份 JavaScript 开发工程师都使用过(例如:setTimeout)。但是,这些 API 并不是由 JavaScript 引擎提供的。那么,它们是谁提供的呢?来自哪里?

auto-orient,1

因此除了引擎,实际上还有很多,如浏览器提供的 Web Api,如:DOMAJAXsetTimeout 等等。

然后,还有非常流行的 event loopcallback queue

JavaScript 是一种单线程编程语言,这意味着它只有一个调用堆栈,因此它一次只能做一件事。

调用栈是一种数据结构,它基本上记录了在程序中的位置。如果进入一个函数,会将它放在栈顶。如果从一个函数返回,会从栈顶推出。这是堆栈可以做的所有事情。

看看下面的代码:

const multiply = (x, y) => {    return x * y;};const square = (x) => {    const s = multiply(x, x);    console.log(s);};square(6);

当引擎开始执行此代码时,调用堆栈开始为空,之后,步骤如下:

auto-orient,1

调用堆栈中的每个条目称为堆栈帧。

这正是抛出异常时堆栈跟踪的构造方式,它基本上是异常发生时调用堆栈的状态。在来看看下面的代码:

function foo() {    throw new Error("foo异常抛出!");}function bar() {    foo();}function start() {    bar();}start();

如果在 Chrome 中执行此操作(假设此代码位于名为 foo.js 的文件中),将生成以下堆栈跟踪:

c16e5d718d7b99da09f28040a758ad61.png

Blowing the stack :当达到最大调用堆栈大小时会发生这种情况,这种情况很容易发生,特别是如果使用递归而没有全面测试。看看这个示例代码:

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

当引擎开始执行这段代码时,它首先调用函数 foo 。然而,这个函数是递归调用的,并且在没有任何终止条件的情况下开始调用自身。因此,在执行的每一步,相同的函数都会一遍又一遍地添加到调用堆栈中。它看起来像这样:

auto-orient,1

然而,在某些时候,调用堆栈中的函数调用数量超过了调用堆栈的实际大小,浏览器决定采取行动,抛出一个错误,它可能如下所示:

c35bedd40e8737d3549ab987103eb723.png

在单线程上运行代码非常容易出现这种情况。

并发和事件循环

如果调用堆栈中的函数调用需要花费大量时间来处理,会发生什么情况?例如,假设想在浏览器中使用 JavaScript 进行一些复杂的图像转换。

如果调用堆栈有要执行的函数,浏览器实际上不能做任何其他事情,这个时候处于阻塞状态。这意味着浏览器无法渲染,无法运行任何其他代码,出现卡住的现象。如果想在应用中使用流畅的 UI,就需要避免阻塞情况的出现。

如果浏览器开始在调用堆栈中处理很多的任务,它可能会在很长一段时间内停止响应,大多数浏览器通过触发错误来终止此类行动,并询问是否要终止网页。

本文简单介绍了 Javascript 的引擎、运行时和调用堆栈,调用堆栈阻塞、卡死的情况将在接下来的《深入了解 V8 引擎》介绍优化技巧。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK