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

注:本文翻译自网上的文章,原文地址:https://blog.sessionstack.com/how-does-javascript-actually-work-part-1-b0bacc073cf

随着JavaScript越来越流行,团队在多个层面对其进行了支持 - 前端、后端、混合应用程序、嵌入式设备等等。

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

如GitHut统计中所示,JavaScript在GitHub中的活动代码库和总推送量方面位居前列,在其他类别也不落后。

image

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

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

概述

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

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

如果您对JavaScript比较陌生,这篇文章将帮助您理解为什么JavaScript与其他语言相比比较“怪异”。

如果您是一位经验丰富的JavaScript开发人员,希望这篇文章能够为您提供一些对于JavaScript运行时是如何工作的全新见解。

JavaScript引擎

一个流行的JavaScript引擎的例子是Google的V8引擎。 例如,在Chrome和Node.js中使用的就是V8引擎。 下面是一个简化了的V8视图:

image

引擎由两个主要组件组成:

  • 内存堆 - 这是内存分配发生的地方
  • 调用堆栈 - 这是代码执行时的堆栈帧

运行时

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

那么,他们从哪里来?

现实世界有点复杂。

image

所以,除了引擎,实际上还有更多,例如那些被浏览器提供的称为Web API的东西,如DOM,AJAX,setTimeout等等。

此外,我们还有使用广泛的事件循环和回调队列。

调用栈

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

调用栈是一个数据结构,它记录了程序执行到哪个地方。 如果进入一个函数,它就放在栈顶。 如果从一个函数返回,会从栈顶弹出,如同所有的堆栈所做的。

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

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

当引擎开始执行这个代码时,调用栈是空的。 之后,步骤如下:

image

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

这正说明了抛出异常时堆栈如何构建的 - 这基本上是异常发生时的调用堆栈的状态。 看看下面的代码:

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

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

image

“堆栈溢出” - 当达到最大调用堆栈大小时会发生这种情况。 这会很容易发生,特别是如果您使用递归且没有广泛地测试代码的时候。 看看这个示例代码:

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

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

image

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

image

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

但是在单线程上运行也有局限。 由于JavaScript只有一个调用栈,当某个事务处理很慢时会发生什么?

并发 & 事件循环

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

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

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

image

现在,这个用户体验很糟糕,不是吗?

那么,我们如何执行密集运算代码而不会阻塞UI并使浏览器无法响应? 解决方案是异步回调。

这将在“JavaScript是如何工作的”教程的第2部分中更详细地阐述:“V8引擎内部+关于如何编写优化代码的5个技巧”。

image

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值