转载自开源中国 译者:Tocy, 凉凉_, 亚林瓜子, 离诌 原文链接

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

随着 JavaScript 变得越来越流行,很多团队在他们的堆栈中实现诸多层级的支持 - 前端、后端、混合应用程序、嵌入式设备等等。

本文是该系列文章的第一篇,旨在深入研究 JavaScript 及其实际工作原理:我们认为通过了解 JavaScript 的构建块以及它们如何一起协作的,你将能够编写更好的代码和应用。

GitHut 统计中所示,JavaScript 在 GitHub 中的活动存储库和总推送量方面位居前列。但它在其他分类中也未落后太多。

(查看 GitHub 语言统计最新版)

如果项目越来越依赖于 JavaScript ,这意味着开发人员必须利用语言和生态系统所提供的所有内容,深入了解其内部,从而构建出令人惊叹的软件。

事实证明,很多开发人员每天都在使用 JavaScript ,但他们并不知道底层会发生什么。

概述

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

在这篇文章中,我们将详细介绍所有这些概念,并解释 JavaScript 是如何运行的。通过了解这些细节,你将能够编写更好的、非阻塞的应用程序,正确使用所提供的 API 。

如果你对 JavaScript 比较生疏,本博客文章将帮助你理解为什么 JavaScript 相比与其他语言更“怪异”。

如果你是一位经验丰富的 JavaScript 开发人员,希望能够为你提供一些关于你每天使用的 JavaScript 运行时的实际工作情况的全新见解。

JavaScript 引擎

Google V8 引擎是一个比较流行的 JavaScript 引擎示例。V8 引擎是在诸如 Chrome 和 Node.js 等内部使用的。下面是对其机制的一个简化视图:

该引擎包括两个主要组件:

* Memory Heap 内存堆 ——  这是内存分配发生的地方

* Call Stack 调用堆栈 ——  这是在你代码执行时栈帧存放的位置

Runtime 运行时

几乎所有的 JavaScript 开发者都使用过浏览器中的 API(例如“setTimeout”)。 但是,这些 API 不是由引擎提供的。

那么,它们从哪里来呢?

事实证明,实际情况有点复杂。

所以,我们有引擎,但实际上还有更多。我们有那些由浏览器所提供的称为 Web API 的东西,比如 DOM、AJAX、setTimeout 等等。

然后,我们还有非常流行的事件循环和回调队列

Call Stack 调用堆栈

JavaScript 是一种单线程编程语言,这意味着它只有一个 Call Stack 。因此,它一次仅能做一件事。

Call Stack 是一个数据结构,它基本上记录了我们在程序中的所处的位置。如果我们进入一个函数,我们把它放在堆栈的顶部。如果我们从一个函数中返回,我们弹出堆栈的顶部。这是所有的堆栈可以做的东西。

我们来看一个例子。看看下面的代码:

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

当引擎开始执行这个代码时,Call Stack 将会变成空的。之后,执行的步骤如下:

Call Stack 的每个入口被称为 Stack Frame(栈帧)。

这正是在抛出异常时如何构建 stack trace 的方法 - 这基本上是在异常发生时的 Call Stack 的状态。看看下面的代码:

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

如果这是在 Chrome 中执行的(假设这个代码在一个名为 foo.js 的文件中),那么会产生下面的 stack trace:

“Blowing the stack”—当达到最大调用堆栈大小时,会发生这种情况。这可能会很容易发生,特别是如果你使用递归,而不是非常广泛地测试你的代码。看看这个示例代码:

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

当引擎开始执行这个代码时,它首先调用函数“foo”。然而,这个函数是递归的,并且开始调用自己而没有任何终止条件。所以在执行的每个步骤中,同一个函数会一次又一次地添加到调用堆栈中。它看起来像这样:



然而,在某些情况下,调用堆栈中函数调用的数量超出了调用堆栈的实际大小,浏览器通过抛出一个错误(如下所示)来决定采取行动:



在单线程上运行代码可能非常容易,因为你不必处理多线程环境中出现的复杂场景,例如死锁。

但是在单线程上运行也是非常有限的。由于JavaScript只有一个调用堆栈,所以当事情很慢时会发生什么?

并发&事件循环

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

你可能会问 - 为什么这会是一个问题?问题是,虽然调用堆栈有要执行的函数,浏览器实际上不能做任何事情 - 它被阻塞了。这意味着浏览器无法渲染,它不能运行任何其他代码,它就是被卡住了。如果你想在你的应用程序中使用流畅的 UI ,这就会产生问题。

而且这并不是唯一的问题。一旦你的浏览器开始在 Call Stack 中处理过多的任务,它可能会停止响应相当长的时间。大多数浏览器会通过触发错误来采取行动,询问你是否要终止网页。

所以,这并不是最好的用户体验,对吗?

那么,我们如何执行大量代码而不阻塞 UI 使得浏览器无法响应? 解决方案就是异步回调。

解读JavaScript 之引擎、运行时和堆栈调用的更多相关文章

  1. 解读 JavaScript 之引擎、运行时和堆栈调用

    https://www.oschina.net/translate/how-does-javascript-actually-work-part-1 随着 JavaScript 变得越来越流行,很多团 ...

  2. 趣谈iOS运行时的方法调用原理

    一个成熟的计算机语言必然有丰富的体系,复杂的容错机制,处理逻辑以及判断逻辑.但这些复杂的逻辑都是围绕一个主线丰富和展开的,所以在学习计算机语言的时候,先掌握核心,然后了解其原理,明白程序语言设计的实质 ...

  3. C#运行时鼠标移动控件 - 调用Windows API(ReleaseCapture)

    [System.Runtime.InteropServices.DllImport("user32.dll")] public static extern bool SendMes ...

  4. OBJC运行时方法替换(Method swizzling)

    在上周associated objects一文中,我们开始探索Objective-C运行时的一些黑魔法.本周我们继续前行,来讨论可能是最受争议的运行时技术:method swizzling.   Me ...

  5. 基于 Java 2 运行时安全模型的线程协作--转

    在 Java 2 之前的版本,运行时的安全模型使用非常严格受限的沙箱模型(Sandbox).读者应该熟悉,Java 不受信的 Applet 代码就是基于这个严格受限的沙箱模型来提供运行时的安全检查.沙 ...

  6. Java运行时内存划分与垃圾回收--以及类加载机制基础

    ----JVM运行时内存划分----不同的区域存储的内容不同,职责因为不同1.方法区:被线程共享,存储被JVM加载的类的信息,常量,静态变量等2.运行时常量池:属于方法区的一部分,存放编译时期产生的字 ...

  7. ASP.NET运行时详解 生命周期入口分析

    说起ASP.NET的生命周期,网上有很多的介绍.之前也看了些这方面的博客,但我感觉很多程序猿像我一样,看的时候似乎明白,一段时间过后又忘了.所以,最近Heavi花了一段时间研究ASP.NET的源代码, ...

  8. Runtime运行时机制

    Runtime 又叫运行时,是一套底层的 C 语言 API,其为 iOS 内部的核心之一,我们平时编写的 OC 代码,底层都是基于它来实现的 我们需要了解的是 Objective-C 是一门动态语言, ...

  9. Cocoa 框架 For iOS(一) 框架的介绍,Objectivie-C运行时能力的解析等 (转载)

    http://blog.csdn.net/totogo2010/article/details/8081253 Cocoa框架是iOS应用程序的基础,了解Cocoa框架,对开发iOS应用有很大的帮助. ...

随机推荐

  1. qt布局管理-缩放、多窗口切换

    关于缩放的布局 所有控件要求与主窗口同时缩放,那么在所有控件设置好布局后,最后点击最外侧的主控件,选择水平布局或者垂直布局即可. 另外每个空间的属性可更改,如SizePolicy用于设置空间是否可缩放 ...

  2. Python之路(第三十五篇) 并发编程:操作系统的发展史、操作系统的作用

    一.操作系统发展史 第一阶段:手工操作 —— 真空管和穿孔卡片 ​ 第一代之前人类是想用机械取代人力,第一代计算机的产生是计算机由机械时代进入电子时代的标志,从Babbage失败之后一直到第二次世界大 ...

  3. hbase_基本命令

    hbase 区分大小写. 1) create '表名','列族名'  --多个列族名用逗号分隔 2)  list  desc(查询表名详细信息) 3)  put '表名','行健','列族:列名',' ...

  4. 构造 this super

    构造方法 我们对封装已经有了基本的了解,接下来我们来看一个新的问题,依然以Person为例,由于Person中的属性都被private了,外界无法直接访问属性,必须对外提供相应的set和get方法.当 ...

  5. 第九周助教工作总结——NWNU李泓毅

    1.助教博客链接: https://www.cnblogs.com/NWNU-LHY/ 2.作业要求博客链接: https://www.cnblogs.com/nwnu-daizh/p/1072688 ...

  6. #pragma常用预处理指令

    #pragma pack(1):1字节对齐#pragma once:指定头文件被编译一次#pragma message("message"):编译时输出message文本#prag ...

  7. idea安装了Mybaits Plugin插件后,启动不起来了

    之前安装了一些插件,谁知道重启完了之后,直接启动不起来了,报错信息如下: cannot load project fatal error initializing plugin com.seven7. ...

  8. web测试和app测试的区别

    功能上: 功能上没有什么区别,都是用同样的方法来写用例(等效.边界值...) 架构上: web是B/S架构(浏览器和服务器)代码更新后数据会同步,可以保证所有客户一致 app是C/S架构(客户端和服务 ...

  9. php实现最简单的MVC框架实例教程

    本文以一个实例的形式讲述了PHP实现MVC框架的过程,比较浅显易懂.现分享给大家供大家参考之用.具体分析如下: 首先,在学习一个框架之前,基本上我们都需要知道什么是mvc,即model-view-co ...

  10. flask-钩子函数&g对象

    常用钩子函数 在Flask中钩子函数是使用特定的装饰器装饰的函数.钩子函数可以在正常执行的代码中,插入一段自己想要执行的代码.那么这种函数就叫做钩子函数.(hook) before_first_req ...