JavaScript是如何工作的:引擎,运行时以及调用栈的概述

原文:How JavaScript works: an overview of the engine, the runtime, and the call stack

译者:neal1991

welcome to star my articles-translator, providing you advanced articles translation. Any suggestion, please issue or contact me

LICENSE: MIT

随着JavaScript变得越来越流行,团队在多个层级都对它进行利用-前端,后端,混合应用,嵌入式设备以及更多。

正如GitHut stats所展示的那样,JavaScript是Github上面最活跃以及总Push次数最多的语言。在其它类别中也不会落后太多。

(获取最新的 GitHub language stats).

如果项目对于JavaScript越来越依赖,这意味着为了构建好的软件开发者必须利用这个JS提供的一切并且对于生态系统的内部有着更深的理解。

因此,尽管每天有很多开发者在使用JavaScript,但并不知道内部到底发生了什么。

概览

几乎每个人都已经听说过V8引擎的概念,并且很多知道JavaScript是单线程的或者它是使用一个回调队列的。

在这篇博文中,我们将会详细讲述所有概念并且解释JavaScript是如何真正运行的。在了解这些细节之后,你将能够写出能够适宜地利用提供的API的更好的,非阻塞的app。

如果对于JvaScript来说还不是很了解,这篇博文将会帮助你理解为什么JavaScript和别的语言相比如此“奇怪”。

如果你是一个有经验的JavaScript开发者,希望这篇文章能够让你对你每天使用的JavaScript Runtime是如何真正工作的。

JavaScript 引擎

最流行的JavaScript引擎的例子之一就是谷歌的V8引擎。比如Chrome以及Node.js内部就是使用V8引擎。下面是一个简单的视图示例:

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

  • 内存堆——这是内存分配发生的地方
  • 回调——这是你代码执行时的栈帧。

Runtime

有很多浏览器中的API几乎都被JavaScript开发者使用过(比如:’setTimeout’)。然而这些API并不是由引擎提供的。

那么,它们是从哪来的呢?

事实证明这有一点复杂。

因此,虽然我们有引擎但实际上是有更多。我们有那些由浏览器提供的Web API,像DOM, AJAX, setTimeout以及更多。

接着,我们还有非常流行的事件循环(event loo)以及回调队列(callback queue)

调用栈

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

调用栈是一种数据结构记录着我们正在程序的什么地方。如果我们步入一个函数,我们就将这个函数放在栈的顶部。如果我们从一个函数返回,我们则是将这个函数从栈顶弹出。这就是这个栈所做的一切。

让我们看一个例子。参看如下代码:

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的文件中),接下来的栈追踪将会产生:

爆栈“——当你达到最大调用栈的大小的时候就会发生这种情况。并且这种情况很容易产生,特别是你没有对你的代码做全面的测试的时候。请看下面的示例代码:

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

当引擎开始执行这段代码的时候,它一开始调用函数”foo“。然而,这个函数递归调用本身并且没有终止条件。因此在每一个执行的步骤中,相同的函数都会一次又一次地被添加到调用栈中。看起来就像这样:

在某个点,然而函数调用的数量就超过调用栈的实际尺寸,那么浏览器就决定采取行动,抛出一个错误,看起来是这个样子的:

在单线程环境中运行代码可能相当容易因为你不需要处理多线程环境中复杂的情形——比如,死锁。

但是在单线程环境中也可能遇到种种限制。因为JavaScript具有一个单独的调用栈,当事情变得缓慢的时候到底发生了什么?

并发以及事件循环

当你的函数调用在调用栈中花了大量的时间来进行到底发生了什么?比如,想象一下假如你想在浏览器中使用JavaScript来做一些复杂的图像转换。

你可能会问——为什么这也会是一个问题?问题是尽管调用栈具有函数来执行,但是浏览器实施中不能做任何其他的事——它被阻塞了。这意味着浏览器不能渲染,它不能运行其他的代码,它就是歇菜了。如果你希望你的app能够具有流畅的UI的时候就会产生问题。

并且这不是唯一的问题。一旦你的浏览器开始处理调用栈中的大量任务,他将在很长时间内都无法响应。大多数浏览器通过抛出错误来采取行动,询问你是否想中止网页。

现在,这并不是一种最好的用户体验,是不是?

因此,我们如何在不阻塞UI并且让浏览器保持响应的情况下执行大量的代码?解决方案就是异步回调

这个将会在”JavaScript是如何工作的”的第二部分进一步解释。

同时,如果你很难在你的JavaScript重现并且理解问题的时候,可以看看 SessionStack。SessionStack记录了你的web应用中的一切:所有的DOM变化,用户交互,JavaScript异常,栈追踪,失败的网络请求以及调试消息。

使用SessionStack,你可以重现你的web应用中的问题就像录像一样,并且可以看到用户交互的一切。

现在有一个免费的计划能够允许你可以开始免费试用

可以扫描二维码或者搜索 mad_coder 关注微信公众号,点击阅读原文可以获取链接版原文。

JavaScript是如何工作的:引擎,运行时间以及调用栈的概述的更多相关文章

  1. JavaScript是如何工作的:引擎,运行时和调用堆栈的概述!

    摘要: 理解JS执行原理. 原文:JavaScript是如何工作的:引擎,运行时和调用堆栈的概述! 作者:前端小智 Fundebug经授权转载,版权归原作者所有. 本文是旨在深入研究JavaScrip ...

  2. JavaScript是如何工作的01:引擎,运行时和调用堆栈的概述!

    概述 几乎每个人都已经听说过 V8 引擎,大多数人都知道 JavaScript 是单线程的,或者它使用的是回调队列. 在本文中,我们将详细介绍这些概念,并解释 JavaScrip 实际如何运行.通过了 ...

  3. 浏览器-09 javascript引擎和Chromium网络栈

    语言的运行 C/C++语言 使用编译器直接将它们编译成本地代码(机器指令),这是由开发人员在代码编写完成之后实施; 用户只是使用这些编译好的本地代码,这些本地代码被系统的加载器加载执行,由操作系统调度 ...

  4. JavaScript 是如何工作的:JavaScript 的共享传递和按值传递

    摘要: 原始数据类型和引用数据类型的副本作为参数传递给函数. 原文:JavaScript 是如何工作的:JavaScript 的共享传递和按值传递 作者:前端小智 Fundebug经授权转载,版权归原 ...

  5. JavaScript 是如何工作的:JavaScript 的内存模型

    摘要: 从内存角度理解 let 和 const 的意义. 原文:JavaScript 是如何工作的:JavaScript 的内存模型 作者:前端小智 Fundebug经授权转载,版权归原作者所有. 这 ...

  6. JavaScript是如何工作的:编写自己的Web开发框架 + React及其虚拟DOM原理

    这是专门探索 JavaScript 及其所构建的组件的系列文章的第 19 篇. 如果你错过了前面的章节,可以在这里找到它们: JavaScript 是如何工作的:引擎,运行时和调用堆栈的概述! Jav ...

  7. JavaScript 是如何工作:Shadow DOM 的内部结构 + 如何编写独立的组件!

    这是专门探索 JavaScript 及其所构建的组件的系列文章的第 17 篇. 如果你错过了前面的章节,可以在这里找到它们: JavaScript 是如何工作的:引擎,运行时和调用堆栈的概述! Jav ...

  8. JavaScript是如何工作的:深入类和继承内部原理 + Babel和TypeScript 之间转换

    这是专门探索 JavaScript 及其所构建的组件的系列文章的第 15 篇. 如果你错过了前面的章节,可以在这里找到它们: JavaScript 是如何工作的:引擎,运行时和调用堆栈的概述! Jav ...

  9. JavaScript是如何工作的: CSS 和 JS 动画底层原理及如何优化它们的性能

    摘要: 理解浏览器渲染. 原文:JavaScript是如何工作的: CSS 和 JS 动画底层原理及如何优化它们的性能 作者:前端小智 Fundebug经授权转载,版权归原作者所有. 这是专门探索 J ...

随机推荐

  1. [LeetCode]-DataBase-Department Top Three Salaries

    The Employee table holds all employees. Every employee has an Id, and there is also a column for the ...

  2. 使用GitHub(一):添加SSHkey

    使用GitHub(一):添加SSHkey 本文简单介绍使用GitHub对代码进行版本控制,包括添加SSHkey.配置Git.使用Git创建版本库并在GitHub上进行管理,主要目的是对学习内容进行总结 ...

  3. ffplay播放PCM裸流

    ffplay -f s16le -ar 48000 -ac 2 d:\lei.pcm

  4. DeepFaceLab 模型预训练参数Pretrain的使用!

    Pretrain参数是20190501版本才加入的参数,作者加入这个参数的目的应该是提升模型的训练速度和增强适应性.具体有哪些提升,需要大家去摸索,我这里分享一下自己的使用过程. ​ 这个参数仅针对S ...

  5. CircleCI 前端自动部署

    概述 现在很多前端库都用到了 CircleCI 进行自动部署,比如Vue,React,作为一个前端我觉得还是有必要实操一下 CircleCI 的,总体来说还是挺简单的,我把过程和体会记录下来,供以后开 ...

  6. Group by的用法

    Group by的用法 GROUP BY 语句根据一个或多个列对结果集进行分组.在分组的列上我们可以使用 COUNT, SUM, AVG,等函数. Group by用法: SELECT column_ ...

  7. jdk的keytool生成jks和获取jks的信息,公匙

    1.生成jks.执行命令:keytool -genkeypair -alias mytest -keyalg RSA -keypass mypass -keystore mytest.jks -sto ...

  8. Windows下对函数打桩,及Linux类似技术

    一个简单的桩实现类: #define JMPCODE_LENGTH 5 //x86 平坦内存模式下,绝对跳转指令长度 #define JMPCMD_LENGTH 1 //机械码0xe9长度 #defi ...

  9. idea 使用过程中的一些设置记录

    1.XML sql底色 在mapper文件中,SQL出现黄色的底色. ** settings ** > ** inspections ** 去掉 ** SQL ** 中的 ** No data ...

  10. Golang基础(4):Go结构体

    当我们要表示同一种数据类型时候,可以用到数组,切片和字典. 当我们要表示不同的数据类型呢?这时候就要用到结构体了 一:定义struct 关键字 type 和 struct 来定义结构体 type st ...