iis服务器助手广告广告
返回顶部
首页 > 资讯 > 前端开发 > node.js >JavaScript的运行原理是什么
  • 738
分享到

JavaScript的运行原理是什么

2024-04-02 19:04:59 738人浏览 安东尼
摘要

这篇文章将为大家详细讲解有关javascript的运行原理是什么,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。扫描器源代码首先被分解成 chunk,每个 c

这篇文章将为大家详细讲解有关javascript的运行原理是什么,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。

扫描器

源代码首先被分解成 chunk,每个 chunk 都可能采用不同的编码,稍后会有一个字符流将所有 chunk 的编码统一为 UTF-16。

在解析之前,扫描器会将 UTF-16 字符流分解成 token。token 是一段脚本中具有语义的最小单元。有不同类型的 token,包括空白符(用于 自动插入分号)、标识符、关键字以及代理对(仅当代理对无法被识别为其它东西时才会结合成标识符)。这些 token 之后被送往预解析器中,接着再送往解析器。

预解析器

解析器的工作量是最少的,只要足够跳过传入的源代码并进行懒解析(而不是全解析)即可。预解析器确保输入的源代码包含有效语法,并生成足够的信息来正确地编译外部函数。这个准备好的函数稍后将按需编译。

解析

解析器接收到扫描器生成的 token 后,现在需要生成一个供编译器使用的中间表示。

首先我们来讨论解析树。解析树,或者说 具体语法树(CST)将源语法表示为一棵树。每个叶子节点都是一个 token,而每个中间节点则表示一个语法规则。在英语里,语法规指的是名词、主语等,而在编程里,语法规则指的是一个表达式。不过,解析树的大小随着程序大小会增长得很快。

相反,抽象语法树 要更加简洁。每个中间节点表示一个结构,比如一个减法运算(-),并且这棵树并没有展示源代码的所有细节。例如,由括号定义的分组是蕴含在树的结构中的。另外,标点符号、分隔符以及空白符都被省略了。你可以在 这里 了解更多 AST 和 CST 的区别。

接下来我们将重点放在 AST 上。以下面用 JavaScript 编写的斐波那契程序为例:

function fib(n) {     if (n <= 1) return n;     return fib(n-1) + fib(n-2);     }

下面的 JSON 文件就是对应的抽象语法

了。这是用 AST Explorer 生成的。(如果你不熟悉这个,可以点击这里来详细了解 如何阅读 jsON 格式的 AST)。

{    "type": "Program",    "start": 0,    "end": 73,    "body": [      {        "type": "FunctionDeclaration",        "start": 0,        "end": 73,        "id": {          "type": "Identifier",          "start": 9,          "end": 12,          "name": "fib"        },        "expression": false,        "generator": false,        "async": false,        "params": [          {            "type": "Identifier",            "start": 13,            "end": 14,            "name": "n"          }        ],        "body": {          "type": "BlockStatement",          "start": 16,          "end": 73,          "body": [            {              "type": "IfStatement",              "start": 20,              "end": 41,              "test": {                "type": "BinaryExpression",                "start": 24,                "end": 30,                "left": {                  "type": "Identifier",                  "start": 24,                  "end": 25,                  "name": "n"                },                "operator": "<=",                "right": {                  "type": "Literal",                  "start": 29,                  "end": 30,                  "value": 1,                  "raw": "1"                }              },              "consequent": {                "type": "ReturnStatement",                "start": 32,                "end": 41,                "argument": {                  "type": "Identifier",                  "start": 39,                  "end": 40,                  "name": "n"                }              },              "alternate": null            },            {              "type": "ReturnStatement",              "start": 44,              "end": 71,              "argument": {                "type": "BinaryExpression",                "start": 51,                "end": 70,                "left": {                  "type": "CallExpression",                  "start": 51,                  "end": 59,                  "callee": {                    "type": "Identifier",                    "start": 51,                    "end": 54,                    "name": "fib"                  },                  "arguments": [                    {                      "type": "BinaryExpression",                      "start": 55,                      "end": 58,                      "left": {                        "type": "Identifier",                        "start": 55,                        "end": 56,                        "name": "n"                      },                      "operator": "-",                      "right": {                        "type": "Literal",                        "start": 57,                        "end": 58,                        "value": 1,                        "raw": "1"                      }                    }                  ]                },                "operator": "+",                "right": {                  "type": "CallExpression",                  "start": 62,                  "end": 70,                  "callee": {                    "type": "Identifier",                    "start": 62,                    "end": 65,                    "name": "fib"                  },                  "arguments": [                    {                      "type": "BinaryExpression",                      "start": 66,                      "end": 69,                      "left": {                        "type": "Identifier",                        "start": 66,                        "end": 67,                        "name": "n"                      },                      "operator": "-",                      "right": {                        "type": "Literal",                        "start": 68,                        "end": 69,                        "value": 2,                        "raw": "2"                      }                    }                  ]                }              }            }          ]        }      }    ],    "sourceType": "module"  }  (来源:GitHub

上面代码的要点是,每个非叶子节点都是一个运算符,而每个叶子节点都是操作数。这棵语法树稍后将作为输入传给 JavaScript 接着要执行的两个阶段。

三个技巧优化你的 JavaScript

下面罗列的技巧清单中,我会省略那些已经广泛使用的技巧,例如缩减代码来最大化信息密度,从而使扫描器更具有时效性。另外,我也会跳过那些适用范围很小的建议,例如避免使用非 ASCII 字符。

提高解析性能的方法数不胜数,让我们着眼于其中适用范围最广泛的方法吧。

1.尽可能遵从工作线程

线程被阻塞会导致用户交互的延迟,所以应该尽可能减少主线程上的工作。关键就是要识别并避免会导致主线程中某些任务长时间运行的解析行为。

这种启发式超出了解析器的优化范围。例如,用户控制的 JavaScript 代码段可以使用 WEB workers 达到相同的效果。你可以阅读 实时处理应用 和 在 angular 中使用 web workers 来了解更多信息。

避免使用大量的内联脚本

内联脚本是在主线程中处理的,根据之前的说法,应该尽量避免这样做。事实上,除了异步和延迟加载之外,任何 JavaScript 的加载都会阻塞主线程。

避免嵌套外层函数

懒编译也是发生在主线程上的。不过,如果处理得当的话,懒解析可以加快启动速度。想要强制进行全解析的话,可以使用诸如 optimize.js(已经不维护)这样的工具来决定进行全解析或者懒解析。

分解超过 100kB 的文件

将大文件分解成小文件以最大化并行脚本的加载速度。“2019 年 JavaScript 的性能开销”一文比较了 Facebook 网站和 Reddit 网站的文件大小。前者通过在 300 多个请求中拆分大约 6MB 的 JavaScript ,成功将解析和编译工作在主线程上的占比控制到 30%;相反,Reddit 的主线程上进行解析和编译工作的达到了将近 80%。

2. 使用 JSON 而不是对象字面量 &mdash;&mdash; 偶尔

在 JavaScript 中,解析 JSON 比解析对象字面量来得更加高效。 parsing benchmark 已经证实了这一点。在不同的主流 JavaScript 执行引擎中分别解析一个 8MB 大小的文件,前者的解析速度最高可以提升 2 倍。

2019 年谷歌开发者大会 也讨论过 JSON 解析如此高效的两个原因:

  1. 鸿蒙官方战略合作共建——HarmonyOS技术社区

  2.  JSON 是单字符串 token,而对象字面量可能包含大量的嵌套对象和 token;

  3.  语法对上下文是敏感的。解析器逐字检查源代码,并不知道某个代码块是一个对象字面量。而左大括号不仅可以表明它是一个对象字面量,还可以表明它是一个解构对象或者箭头函数。

不过,值得注意的是,JSON.parse 同样会阻塞主线程。对于超过 1MB 的文件,可以使用 FlatBuffers 提高解析效率。

3. 最大化代码缓存

最后,你可以通过完全规避解析来提高解析效率。对于服务端编译来说, WebAssembly (WASM) 是个不错的选择。然而,它没办法替代 JavaScript。对于 JS,更合适的方法是最大化代码缓存

值得注意的是,缓存并不是任何时候都生效的。在执行结束之前编译的任何代码都会被缓存 &mdash;&mdash; 这意味着处理器、监听器等不会被缓存。为了最大化代码缓存,你必须最大化执行结束之前编译的代码数量。其中一个方法就是使用立即执行函数(IIFE)启发式:解析器会通过启发式的方法标识出这些 IIFE 函数,它们会在稍后立即被编译。因此,使用启发式的方法可以确保一个函数在脚本执行结束之前被编译。

此外,缓存是基于单个脚本执行的。这意味着更新脚本将会使缓存失效。V8 团队建议可以分割脚本或者合并脚本,从而实现代码缓存。但是,这两个建议是互相矛盾的。你可以阅读“JavaScript 开发中的代码缓存”来了解更多代码缓存相关的信息。

结论

解析时间的优化涉及到工作线程的延迟解析以及通过最大化缓存来避免完全解析。理解了 V8 的解析机制后,我们也能推断出上面没有提到的其它优化方法。

下面给出了更多了解解析机制的资源,这个机制通常来说同时适用于 V8 和 JavaScript 的解析。

  •  V8 文档

  •  V8 博客

  •  V8-perf

额外小贴士:理解 JavaScript 的错误和性能是如何影响你的用户的。

跟踪生产过程中 JavaScript 的异常或者错误是很耗时的,而且也很令人伤脑筋。如果你有兴趣监控 JavaScript 的错误和应用性能是如何对用户造成影响的,可以尝试使用 LogRocket。

JavaScript的运行原理是什么

LogRocket 就像是为 web 应用量身订造的 DVR(录像机),它可以确切地记录你的网站上发生的所有事情。LogRocket 可以帮助你统计并报告错误,以查看错误发生的频率以及它们对你的用户群的影响程度。你可以轻松地重现错误发生时特定的用户会话,以查看是用户的哪些操作导致了 bug。

LogRocket 可以记录你的 app 上的请求和响应(包含 header 和 body)以及用户相关的上下文信息,从而窥探问题全貌。它也可以记录页面的 htmlCSS,即使是面对最复杂的单页面应用,也可以重构出像素完美级别的视频。

关于JavaScript的运行原理是什么就分享到这里了,希望以上内容可以对大家有一定的帮助,可以学到更多知识。如果觉得文章不错,可以把它分享出去让更多的人看到。

--结束END--

本文标题: JavaScript的运行原理是什么

本文链接: https://www.lsjlt.com/news/69924.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • JavaScript的运行原理是什么
    这篇文章将为大家详细讲解有关JavaScript的运行原理是什么,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。扫描器源代码首先被分解成 chunk,每个 c...
    99+
    2022-10-19
  • JavaScript 引擎的运行原理是什么
    这篇文章将为大家详细讲解有关JavaScript 引擎的运行原理是什么,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。注意:本文主要基于 Node.js 和基...
    99+
    2022-10-19
  • JavaScript运行机制及原理是什么
    这篇文章主要介绍“JavaScript运行机制及原理是什么”,在日常操作中,相信很多人在JavaScript运行机制及原理是什么问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”...
    99+
    2022-10-19
  • Servlet的运行原理是什么
    这篇文章主要介绍“Servlet的运行原理是什么”,在日常操作中,相信很多人在Servlet的运行原理是什么问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Servlet的运行原理是什么”的疑惑有所帮助!接下来...
    99+
    2023-06-16
  • laravel运行的原理是什么
    Laravel是一个基于PHP的开源Web应用框架,它的运行原理如下: 路由:Laravel使用路由来确定请求应该由哪个控制器处...
    99+
    2023-10-28
    laravel
  • wepy运行原理是什么
    这篇文章主要讲解了“wepy运行原理是什么”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“wepy运行原理是什么”吧!分析源码之前,我们先来回顾一下,wepy 的使用:<!-- ...
    99+
    2023-06-26
  • Spring Boot的运行原理是什么
    本篇内容介绍了“Spring Boot的运行原理是什么”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!导读Spring Boot方式的项目开发...
    99+
    2023-06-02
  • docker运行的底层原理是什么
    这篇文章将为大家详细讲解有关docker运行的底层原理是什么,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。     docker是一个client-server结构的系统,d...
    99+
    2023-06-04
  • Nodejs 中libuv运行的原理是什么
    Nodejs 中libuv运行的原理是什么,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。1) libuv的架构2) 案例,从细节的角度看lib...
    99+
    2022-10-19
  • JS的运行机制原理是什么
    本篇内容主要讲解“JS的运行机制原理是什么”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“JS的运行机制原理是什么”吧!代码块: JS中的代码块是指由<script>标签分割...
    99+
    2023-06-17
  • Python中Flask运行的原理是什么
    这篇文章主要介绍“Python中Flask运行的原理是什么”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“Python中Flask运行的原理是什么”文章能帮助大家解决问题。  所有的 Python W...
    99+
    2023-06-29
  • JavaScript引擎的运行原理
    这篇文章主要讲解了“JavaScript引擎的运行原理”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“JavaScript引擎的运行原理”吧!一些名词JS引擎...
    99+
    2022-10-19
  • JavaScript的运行原理怎么掌握
    这篇文章主要讲解了“JavaScript的运行原理怎么掌握”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“JavaScript的运行原理怎么掌握”吧!了解Ja...
    99+
    2022-10-19
  • JAVA和JVM运行原理是什么
    一、Java运行原理 (1)源码编写:Java源代码是由开发人员使用Java语言编写的,它是按照Java的语法规则进行编写的文本文件,一般以“、java”为文件扩展名。 (2)源码编译:Java源代码通过Java编译器进行编译,...
    99+
    2023-10-29
    原理 JAVA JVM
  • laravel框架运行原理是什么
    Laravel框架的运行原理主要是基于MVC(模型-视图-控制器)架构模式。以下是Laravel框架的运行原理的基本步骤:1. 路由...
    99+
    2023-09-06
    laravel
  • 前端 JavaScript运行原理
    目录1、什么是JavaScript引擎2、V8引擎3、运行时环境4、运行时的调用栈5、异步任务6、总结1、什么是JavaScript引擎 JavaScript引擎是一个计算机程序,它...
    99+
    2022-11-12
  • php的运行机制和原理是什么
    PHP的运行机制和原理如下: 客户端请求:当用户在浏览器中输入一个URL并发送请求时,服务器会接收到该请求。 Web服务器:...
    99+
    2023-10-28
    php
  • Hibernate在Java中的运行原理是什么
    这篇文章将为大家详细讲解有关Hibernate在Java中的运行原理是什么,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。什么是Hibernate?      H...
    99+
    2023-05-31
    hibernate java ava
  • Linux内核的运行原理是什么呢
    Linux内核的运行原理是什么呢,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。众所周知的是,几乎整个互联网都运行在 Linux 上,从网络协议,到服务器,到你平常访问的绝大多数...
    99+
    2023-06-16
  • Servlet的定义及运行原理是什么
    今天小编给大家分享一下Servlet的定义及运行原理是什么的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。1.什么是servl...
    99+
    2023-07-05
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作