发布于 2026-01-06 5 阅读
0

JavaScript 是编译型语言还是解释型语言?DEV 全球项目展示挑战赛(由 Mux 赞助):快来展示你的项目吧!

JavaScript是编译型语言还是解释型语言?

由 Mux 主办的 DEV 全球展示挑战赛:展示你的项目!

介绍

你可能听说过 JavaScript 是一种解释型语言,但也有人认为它不是解释型语言,而是一种编译型语言。

这可能会令人困惑。

我以前经常感到困惑,因为每当我阅读有关 JavaScript 是解释型语言还是编译型语言的书籍或文章时,都会得到相互矛盾的答案。但事实是,这取决于语言的具体实现方式。

让我解释一下。

为了阐明这一点,我们首先来了解一下编译器和解释器是什么,以及它们之间的区别。

编译器

编译器是一种软件工具,它将人类可读的源代码翻译成机器可执行的代码。编译过程包括多个阶段,包括:

  • 词汇分析

  • 语法分析(解析)

  • 语义分析

  • 代码生成

  • 优化

在词法分析过程中,编译器会将源代码分解成一系列词法单元(token),每个词法单元都是一个独立的含义单位。例如,在语句 `if` 中"x = 5 + 3;",词法单元包括"x"`a`、"="` "5"b`、` "+"c`、`d` 和"3"`e`。词法分析器还会为每个词法单元分配一个类型,例如“标识符”、“运算符”或“字面量”。

然后,在语法分析(解析)过程中,编译器会检查词法单元的顺序和结构,以确保它们构成一个有效的程序。这包括分析编程语言的语法,并构建一个表示程序结构的语法树。

语法分析完成后,编译器会进行语义分析,检查代码中的逻辑错误。这包括验证程序是否遵循语言规则,例如类型检查、作用域规则和函数调用。

编译器验证程序正确性后,会生成可供计算机执行的机器代码或字节码。这涉及到将语法树和语义信息翻译成计算机硬件可以执行的底层指令。

最后,编译器会执行优化以提高生成代码的效率。此过程包括分析代码并进行修改以改善内存使用。

总而言之,编译器是将人类可读的源代码翻译成机器可执行代码的必要工具。编译过程包含多个阶段,每个阶段对于生成正确高效的代码都至关重要。

口译员

解释器是一种程序,它直接读取并执行用高级编程语言编写的代码,无需中间编译步骤。与编译器在执行前将整个源代码翻译成机器代码不同,解释器逐行读取并执行源代码,遇到每个语句时就进行翻译和执行。

当解释器运行程序时,

  • 它首先对源代码进行词法分析和解析,生成表示程序结构的抽象语法树(AST)。

  • 解释器随后遍历抽象语法树(AST),依次执行每个节点。在执行过程中,解释器可能会生成和操作额外的数据结构来表示程序的状态,例如用于跟踪变量值的符号表。

使用解释器的一个优势在于它支持更动态、更具交互性的开发,程序员可以即时测试和修改代码,而无需重新编译整个程序。然而,解释代码的速度可能比执行已编译的代码慢,因为解释器需要执行额外的工作来翻译和执行每一条语句。

总的来说,解释器提供了一种便捷的方式来执行高级语言代码,但在某些情况下,其性能可能不如编译后的代码。

编译器与解释器的简单比较

编译器与解释器的简单比较

关于编译器和解释器有很多技术信息,所以我们尽量用简单易懂的方式来讲解:

就像生活中的其他事情一样,让我们​​一边喝茶一边试着理解它。

假设你想泡一杯红茶,你打开谷歌搜索配料表,结果如下:

  • 红茶茶叶

  • 糖(可选)

  • 牛奶(可选)

泡茶有两种方法,一种是编译器方法,另一种是解释器方法。

制作茶叶的人首先会在冲泡之前,将所有原料摆放在面前,包括每种原料的具体用量,然后才会冲泡所有准备好的茶叶成分。

翻译员会拿起杯子,开始逐行念出配料表。他会烧开水,放入茶叶,浸泡几分钟,如果需要可以加糖或牛奶,然后将茶水过滤到杯子里。

编译器的构建(准备)时间会比解释器的构建(准备)时间长。但是,编译器的运行(加载)时间会短得多。

既然你已经了解了它们的区别,我们来谈谈 JavaScript 吧。

JavaScript 是编译型语言还是解释型语言?

我刚开始学习 JavaScript 的时候,和许多人一样,被告知它是一种解释型语言,而且我很长一段时间都深信不疑。然而,经过进一步研究后,我意识到这种说法并不完全准确。

虽然许多人仍然认为 JavaScript 是一种解释型语言,但有证据表明它实际上是一种编译型语言。JavaScript 引擎处理 bug 的方式就体现了这一点。

当你的 JavaScript 代码中出现语法错误时,引擎通常会在代码执行之前就发出警告。这表明代码在执行前会经过编译和验证,这是编译型语言的特性。

您可以自行尝试,在浏览器中打开以下 HTML 代码并查看控制台:



console.log("Hello World from javascript!");
 console.log("Hello World from javascript!");
 console.log("Hello World from javascript!");
 console.log("Hello World from javascript!");
 console.log("Hello World from javascript!");
 console.log("Hello World from javascript!");
 console.log("Hello World from javascript!");
 console.log("Hello World!);


Enter fullscreen mode Exit fullscreen mode

最后一行代码缺少一个右引号,这应该会触发语法错误。然而,如果 JavaScript 真的是一种解释型语言,那么在抛出错误之前,控制台应该已经打印出前九行代码了。

程序却立即崩溃了。



Uncaught SyntaxError: Invalid or unexpected token


Enter fullscreen mode Exit fullscreen mode

这表明它在执行前已经过编译和验证。

虽然关于 JavaScript 是否真正是一种编译型语言仍然存在争议,但很显然,称其为解释型语言并不完全准确。

证明 JavaScript 并非完全解释型语言的另一种方法是通过“解释”的概念hoisting。例如,考虑以下代码:



max(1, 2);
// 2
function max(num1, num2){
  return num1 > num2 ? num1 : num2;
}


Enter fullscreen mode Exit fullscreen mode

在传统编程语言中,函数必须先定义才能被调用。然而,在 JavaScript 中,函数可以在定义之前就被调用。这种行为被称为“先定义后调用” hoisting,它是 JavaScript 工作方式的一个基本特性。



Note: We will explore more about `hoisting` in other blog for now this explanation is enough.


Enter fullscreen mode Exit fullscreen mode

JavaScript 引擎如何max在执行到函数声明之前就知道该函数的存在?对此,唯一合理的解释是代码必须先经过编译才能执行。

所以,JavaScript 是一种编译型语言,对吗?

JavaScript 是一种编译型语言吗?

嗯,情况比较复杂。过去,每种编程语言都很容易归类为编译型语言或解释型语言。然而,随着现代源代码运行方式的出现,一种“中间”领域就此产生。

JavaScript 代码是如何被翻译的?

自 JavaScript 诞生之初作为一种简单的脚本语言以来,它已经取得了长足的进步。早期,JavaScript 引擎只是解释器,逐行执行代码。然而,随着 JavaScript 的普及和应用场景的扩展,性能问题也随之出现。实时解释代码会导致性能损失,尤其是在复杂应用中。这促使人们开发出使用即时 (JIT) 编译器的新型引擎。

JIT 编译器与传统编译器(例如用于 C++ 的编译器)不同。传统编译器有充足的时间在编译过程中优化代码,而 JIT 编译器必须在代码执行前立即进行编译。JavaScript 代码一旦即将运行,就会被编译成可执行的字节码。

尽管 JavaScript 的编译方式与其他编译型语言有所不同,但其编译过程仍然遵循一些与传统编译器相同的规则。JavaScript 代码在执行前会被解析,使其看起来像是一种已解析的语言。然而,实际上,代码在执行前会被转换成二进制形式。

为了更好地理解这个过程,让我们仔细看看代码执行在幕后的工作原理:

  • 首先,使用 Babel 或 Webpack 等工具对代码进行转译。

  • 转译后的代码被交给引擎,引擎将其解析为抽象语法树(AST)。

  • 然后,抽象语法树(AST)被转换成机器可以理解的字节码。这就是中间表示(IR),它会被即时编译器(JIT)进一步优化。

  • 优化完成后,JS虚拟机(VM)执行代码。



Some people argue that the JS VM is "interpreting" the bytecode, but this is not entirely accurate. If we were to use that definition, we would also have to say that Java, which is another JVM-driven language, is also interpreted. 


Enter fullscreen mode Exit fullscreen mode

因此,我们可以得出结论,JavaScript 的执行分为三个阶段:

  • 解析

  • 编译

  • 执行

JavaScript 代码在执行之前会先进行解释执行。例如,考虑以下代码:



console.log("Hello");
for(var i = 0;i<4;i++) {
    console.log(Hello);
}


Enter fullscreen mode Exit fullscreen mode

上述代码示例中,输出结果展示了解释器的行为:首先Hello在控制台打印内容,然后报告错误。该输出结果有力地证明了 JavaScript 是一种解释型语言。

虽然 JavaScript 代码看起来像是逐行执行的,但这仅在解析阶段成立。实际上,所有代码都会被一次性编译成机器可读的代码,然后再执行。因此,JavaScript 是一种即时编译语言,它在第一阶段使用解释器。



Note: JavaScript can operate in an interpreted manner in older browsers. However, every modern browser currently supports "JIT", so JavaScript code is always compiled. Whether JavaScript is compiled or interpreted depends on the environment in which it is run. If it runs in older browsers, it's interpreted. If it runs in modern browsers, it's compiled.


Enter fullscreen mode Exit fullscreen mode

解释 JavaScript 中的 JIT

现在我们已经对 JavaScript 的工作原理和 JIT 编译器有了基本的了解,接下来让我们深入了解 JIT 编译器的工作原理以及它们为何如此重要。

但别担心,我们不会使用太多专业术语。相反,我们将用简单的语言来探讨 JIT 编译器,以了解它们的工作原理、重要性以及最初引入它们的原因。

JIT(即时)编译是现代 JavaScript 引擎用来提升 JavaScript 代码性能的一种技术。与在编译过程中优化代码的传统编译器不同,JIT 编译器采用了一种折衷方案。它首先解释执行代码,然后选择性地编译那些重复使用的部分。这种方法兼顾了快速启动和性能提升。

当 JavaScript 引擎遇到一个函数时,它可以选择即时解释执行,也可以选择先编译再执行。即时解释执行虽然启动速度更快,但整体性能会更慢,因为每次函数执行时都需要逐行翻译代码。而先编译再执行虽然启动速度较慢,但​​由于机器代码优化,最终执行速度可能会更快。

在这种方法中,代码被编译成机器代码,然后由处理器执行。

JIT编译器采用了一种折衷方案,它首先解释执行代码,然后选择性地编译那些重复使用的部分。因此,JIT编译器能够在快速启动和更佳性能之间取得平衡。

JIT编译器的进程

为了理解 JIT 编译器的工作原理,我们来看一个例子:



function add(a, b) {
  return a + b;
}

for (let i = 0; i < 100000000; i++) {
  add(i, i + 1);
}


Enter fullscreen mode Exit fullscreen mode

这段代码包含一个简单的add函数,用于将两个数字相加,以及一个循环,该循环100使用不同的输入调用此函数一百万次。

当 JavaScript 引擎首次遇到这段代码时,它会立即解释并执行。然而,随着循环重复运行,引擎会检测到该add函数被重复调用,并决定将其编译成机器代码以提高性能。

编译JIT器会分析代码,并针对循环中使用的特定输入进行优化。

例如,它可能会决定内联add函数,这意味着用实际的加法代码替换函数调用,从而消除调用函数的开销。此外,它还可能执行其他优化,例如常量折叠或死代码消除,以去除不必要的计算。

总的来说,JIT 编译器通过选择性地编译重复使用的 JavaScript 代码部分来帮助提高 JavaScript 代码的性能,同时还能保持快速的启动时间。

现在我们对 JIT 编译器的工作原理有了更深入的了解,让我们来深入探讨之前讨论过的问题及其根本原因。

为什么会出现执行前语法错误警报

让我们用之前讨论过的例子来探究 JavaScript 中语法错误警告背后的原因。



console.log("Hello World from javascript!");
 console.log("Hello World from javascript!");
 console.log("Hello World from javascript!");
 console.log("Hello World from javascript!");
 console.log("Hello World from javascript!");
 console.log("Hello World from javascript!");
 console.log("Hello World from javascript!");
 console.log("Hello World!);


Enter fullscreen mode Exit fullscreen mode

就提供的代码示例而言,JavaScript 引擎会在执行前编译代码,语法错误是在编译阶段检测到的。这是因为该引擎使用的JIT是实时优化编译器,它会在代码执行过程中进行优化。当引擎在编译阶段遇到错误时,它会立即抛出错误,而不会执行任何代码。

因此,JavaScript 在编译阶段检测语法错误这一事实表明,它更像是一种编译型语言,而不是一种解释型语言。

吊装的故事是怎样的?

JIT现在让我们结合之前的代码,来理解引擎上下文中的提升机制:



max(1, 2);
// 2
function max(num1, num2){
  return num1 > num2 ? num1 : num2;
}


Enter fullscreen mode Exit fullscreen mode

max那么,JavaScript 引擎是如何在到达函数声明之前就知道该函数的呢?

JavaScript 中的提升机制是如何工作的?

JIT答案在于现代浏览器中执行 JavaScript 代码时在后台发生的即时 (JIT ) 编译过程。

在优化阶段,JIT编译器可以分析代码,确定哪些函数可能被频繁调用,哪些变量可能被重复访问。通过识别这些模式,编译器可以优化代码以加快执行速度,从而显著提升性能。

对于函数提升的情况,编译器可以识别出函数在其作用域顶部声明的模式,并据此优化代码。

结论

总而言之,虽然人们通常认为 JavaScript 是一种解释型语言,但它实际上是一种即时编译型语言。现代 JavaScript 引擎使用JIT编译器来优化代码的执行。与传统的解释型语言相比,编译器使得 JavaScript 的执行速度更快、效率更高。虽然 JavaScript 的编译方式与其他编译型语言有所不同,但它仍然遵循一些反映编译过程的规则。

JavaScript 代码在执行前会被解析,这使其看起来像是一种已解析的语言,但实际上,代码在执行前会被转换成二进制(计算机硬件直接执行的机器代码)形式。这个转换过程包含多个步骤,包括代码转译、将其解析为抽象语法树(ASTT AST)、将其转换为字节码,以及使用JIT编译器进行优化。

所以,要回答这个问题,"Is javascript compiled or interpreted language?"答案是两者兼而有之。在旧版浏览器中,它是解释执行的;而在现代浏览器中,它是借助JIT编译器编译执行的。


如果您喜欢这篇文章并觉得它对您有所帮助,可以请我喝杯咖啡以示感谢。您的支持对我意义重大,也激励我继续为您创作有价值的内容。谢谢!

请我喝杯咖啡

欢迎访问我的个人博客,我在那里发布更多类似的内容:Roblog

资源:

我的其他帖子:

文章来源:https://dev.to/robiulhr/is-javascript-compiled-or-interpreted-language-l20