JavaScript如何在幕后工作?JS引擎和运行时解释

319 阅读7分钟

所以您可能知道您的代码以某种方式在您的浏览器中编译和执行以显示您构建的漂亮的 Web 应用程序。但是您是否知道启用输出的所有组件?

让我们深入了解幕后的 JavaScript。您无法确切看到的抽象部分。

为什么一个看似抽象的主题对你来说很重要?了解 JavaScript 的内部工作原理可以让您超越表面层次,从更深层次的角度探索这门语言。

它提供有关语言的上下文信息以及 JavaScript 引擎如何优化代码。这将为您提供一些重要的基础知识,这些知识会影响您编写代码的方式。它还可以帮助您编写更高效、可扩展和可维护的代码。

(JavaScript教程:java567.com/search.html?sWord=javascript&v=2306012)

JavaScript 引擎

09BA18A6-3F7A-4DBE-AA43-C482725CA5E4显示调用堆栈和堆的 JavaScript 引擎

JavaScript 引擎只是一个解释 JavaScript 代码的计算机程序。引擎负责执行代码。

每个主流浏览器都有一个执行 JavaScript 代码的 JavaScript 引擎。最流行的是 Google Chrome V8引擎。Google 的 V8 为 Google Chrome 和Node.js提供支持,Node.js 是用于构建服务器端应用程序的后端 JavaScript 运行时环境。

其他主要浏览器引擎包括:

  • Mozilla 为 Firefox 开发的 SpiderMonkey
  • 为 Safari 浏览器提供支持的 JavaScriptCore
  • 为 Internet Explorer 提供动力的脉轮

任何 JavaScript 引擎通常都包含一个调用堆栈和一个堆。调用栈是代码执行的地方。堆是一个非结构化的内存池,用于存储应用程序所需的所有对象。

由于计算机的处理器只能理解二进制、0 和 1,因此必须将代码转换为 0 和 1。

当代码片段进入引擎时,代码首先被解析,即被读取。该代码随后被解析为称为抽象语法树 (AST) 的数据结构。然后使用生成的树来创建机器代码。

使用执行上下文在 JavaScript 引擎调用堆栈中执行。这是执行 JavaScript 代码的环境。

FA4EDBD9-0348-4445-B795-8D1FEF904CBE显示 JavaScript 执行过程的图表说明

JavaScript 运行时

将 JavaScript 运行时视为包含运行 JavaScript 所需的所有组件的房子。这个房子包括 JavaScript 引擎、Web API 和回调队列。

Web API 是提供给引擎但不属于 JavaScript 语言的功能。引擎可以通过浏览器访问它们,并有助于访问数据或增强浏览器功能。示例是文档对象模型 (DOM) 和获取 API。

CDFBBA53-5533-478E-91CE-5904714E1043浏览器中的 JavaScript 运行时图,包含 JavaScript 引擎、WEB API 和回调队列

回调队列包括准备好执行的回调函数。回调队列确保回调以先进先出 (FIFO) 方法执行,并在堆栈为空时将其传递到堆栈中。

浏览器运行时和 Node.js 是运行时环境的示例。

当 JavaScript 在 Web 浏览器中执行时,它是在浏览器的运行时环境中运行的。浏览器运行时环境提供对 DOM 的访问,DOM 允许与网页元素交互、处理事件和操纵页面结构。

Node.js 提供了一个服务器端运行时环境,用于在浏览器外部执行 JavaScript。因为它在浏览器外部执行 JavaScript,所以它无权访问 Web API。相反,Node.js 运行时环境将其替换为称为 C++ 绑定和线程池的东西。

JavaScript 优化策略

现代 JavaScript 引擎采用了一些优化策略来提高代码执行的性能。这些优化在执行过程中动态发生。让我们看看其中的一些策略。

即时编译

涉及将 JavaScript 代码转换为机器代码的过程使用编译和解释进行。

在编译中,将整个源代码一次性转换为机器码,写入二进制文件,供计算机执行。

EB039874-52DC-4CD8-B95C-F9E75F2D2283_4_5005_c显示代码编译过程的图表

相反,在解释期间,解释器逐行解释源代码,并在遇到它时执行每一行。

D3DC97A6-3D79-46E0-A2F2-BB3FA694F0EF_4_5005_c显示代码解释过程的图表

JavaScript 曾经是一种解释型语言,但解释型语言与编译型语言相比速度较慢。

为了优化 Web 应用程序的性能,JavaScript 结合了编译和解释。这称为即时编译。该方法一次性将全部代码编译成机器码并执行。

E2BA4399-5F52-408C-B2AB-A9E6F74B3238_4_5005_c显示代码的即时编译的图表

即时编译涉及与常规编译相同的两个过程,但这里的机器代码没有写入二进制文件。代码也是在编译后立即执行的。

这对 JavaScript 中的代码执行速度产生了重大影响。所以希望这有助于消除 JavaScript 是一种纯解释型语言的观念。

为了完全优化 JavaScript 代码,引擎首先创建一个未优化版本的机器代码,以便它可以立即开始执行。在此期间,代码正在重新优化并在当前运行的程序执行的后台重新编译。这是多次完成以产生最终的、最优化的版本。

解析、编译和执行的过程发生在引擎中无法从代码访问的一些特殊线程中。

什么是内联?

内联是 JavaScript 用来提高性能和速度的另一种优化技术。

 function add(a, b) {
   return a + b;
 }
 ​
 let result = 0;
 result = result + 5;
 result = result + 3;
 ​
 console.log(result); //

add()在此代码段中,未直接调用原始函数。相反,函数内的代码return a + b;被插入到调用站点。

这种优化特别针对重复调用的函数。JavaScript 引擎将像往常一样运行该函数。但是由于函数经常被调用,引擎会在调用站点用函数的实际代码替换函数调用。这有助于防止多个函数调用并提高性能。

性能注意事项

有几个因素会影响 Web 应用程序的性能。由于 JavaScript 引擎采用一些策略来确保优化,因此开发人员还需要考虑一些最佳实践以实现高效执行。

最小化 DOM 操作和减少函数调用等技术可增强代码性能。

频繁访问和交互 DOM 会减慢网页的呈现速度并导致性能滞后。由于您无法完全避免与 DOM 交互,因此您可以通过批处理 DOM 更新来最小化交互以减少开销。

此外,减少函数调用会降低性能。通过减少函数调用,您可以简化代码并使其更高效,从而使您的 JavaScript 应用程序更快、响应更快。

 // Inefficient code with unnecessary function calls
 function calculateTotal(a, b, c) {
   return addNumbers(a, b) + multiplyNumbers(c, b);
 }
 function addNumbers(x, y) {
   return x + y;
 }
 function multiplyNumbers(x, y) {
   return x * y;
 }
 // Improved code with reduced function calls
 function calculateTotal(a, b, c) {
   const sum = a + b;
   return sum + c * b;
 }
 console.log(calculateTotal(2, 3, 4)); // Output: 23

在低效代码中,该函数对和calculateTotal()进行单独的函数调用。这会导致函数调用开销。addNumbers()``multiplyNumbers()

在改进后的代码中,通过直接在函数内进行加乘运算,减少了函数调用calculateTotal()。通过减少函数调用,代码变得更加高效并提高了执行速度。

未来的 JavaScript 发展和趋势

JavaScript 引擎和运行时环境将继续得到改进和进步。这些更改旨在提高 Web 应用程序的性能。

其中一个进步是WebAssembly的兴起。WebAssembly 为 Web 应用程序带来了接近本机的性能,并支持多种语言。它为性能优化和执行速度开辟了新的可能性。

对于 JavaScript 开发人员来说,紧跟这些趋势并相应地调整新的编码最佳实践非常重要。

结论

在呈现功能性 Web 应用程序之前,JavaScript 代码的解析方式涉及很多过程。

本文提供了主要概念的高级概述。它解释了 JavaScript 引擎如何执行代码、运行时及其组件。它还继续解释优化策略并强调性能注意事项。

(JavaScript教程:java567.com/search.html?sWord=javascript&v=2306012)