JavaScript - 编译型语言吗?

7

我是一名新手网页开发者,正在学习 JavaScript。

从斯坦福大学的一门课程中了解到:

JavaScript 是一种解释型语言,而不是编译型语言。像 C++ 或 Java 这样的程序需要在运行之前进行编译。源代码通过一个称为编译器的程序传递,编译器将其转换为机器能理解和执行的字节码。相比之下,JavaScript 没有编译步骤。相反,浏览器中的解释器会读取 JavaScript 代码,解释每一行并运行它。更现代的浏览器使用一种称为即时编译(JIT)的技术,可以在运行时将 JavaScript 编译成可执行的字节码。

还有来自《你不知道的 JavaScript:作用域与闭包》作者Kyle Simpson的说法:

……尽管 JavaScript 属于“动态”或“解释性”语言的一般类别,但实际上它是一种编译语言。

为简单起见,我们假设任何 JavaScript 代码片段都必须在执行之前(通常是紧接着!)编译。因此,JS 编译器将首先编译程序 var a = 2;,然后准备好执行它,通常会立即执行。

还有从 Stack Overflow 的一些问题中得出的一些想法:这取决于语言的实际实现。

你有什么想法吗?


https://dev59.com/aWkw5IYBdhLWcg3w1d4m - Thilo
你有那本书中下一个句子吗?他们在那里真正解释了关于“编译语言”的断言吗? - Thilo
JIT的描述是错误的。自从第一个原型实现(LiveScript)以来,Javascript一直被编译为字节码。JIT指的是将该字节码编译为本机机器代码 - 与Java使用JIT术语的含义相同。 - slebetman
@slebetman 那么,我应该在哪里学习JS理论呢? - haitran
@haitran:不幸的是,在我看来,没有一个单一的地方。我花了几年时间使用JavaScript开发出对这种语言的感觉。一旦你了解它,它是一种很好的语言。但由于其历史,它并不是一种完全记录的语言。即使规范也没有真正记录实现细节,比如“它是否被编译”。也没有记录执行的两个阶段:编译和评估的存在。我们大多数老派js程序员都是通过编写测试程序并在不同的浏览器中运行它们来学习它的。 - slebetman
显示剩余4条评论
3个回答

5
Chrome浏览器使用V8引擎编译JavaScript,其他浏览器可能使用Rhino或SpiderMonkey。
V8是由Google用C++编写的JavaScript引擎。它用于在客户端(Google Chrome)和服务器端(node.js)应用程序中编译JS。为了获得速度,V8将JavaScript代码转换为更高效的机器码,而不是使用解释器。
V8通过实现JIT(即时编译器)编译JavaScript代码成机器码,在脚本执行时进行,就像许多现代JavaScript引擎(如SpiderMonkey或Rhino(Mozilla))所做的一样。与V8的主要区别在于,它不生成字节码或任何中间代码。它只是即时编译JavaScript。
希望这能帮到你!

2

好的,你可以可能会涉及语义和术语的差异,但有两个重要的点:

  • Javascript(在网页中)以其源代码形式分发(或至少以最小化的文本形式),而不是作为二进制编译的提前。

  • Javascript即使是由浏览器执行,也不会被编译为可执行的机器代码(尽管其中的某些部分可能是出于性能优化目的这些天),而是通过虚拟机执行。


Chrome(V8),Safari(squirrelfish)和IE9+都有JIT编译器,可以像Java一样将字节码的部分编译为本机机器代码。事实上,较新的Safari使用llvm作为其JavaScript“解释器”- llvm是C / C ++ / C#/ Pascal / Fortran等编译器。 - slebetman

0
在这个上下文中,“编译”一词指的是一种通常被认为是直接翻译成本地机器语言或格式的语言。典型的情况包括C和C++。但是,请注意,这两种语言也可以被解释执行。C解释器的一个例子是Pico C,它处理C的子集。
因此,真正的问题是关于环境的。一种语言可以在编译或解释环境中运行。通过以下测试可以清楚地区分这两种情况:
    Does the language possess a "command level" mode
    in which forward references are inherently impossible?

请花一点时间考虑这个问题。解释性语言是实时读取其规范的语言。前向引用是指在规范制定时不存在的东西。由于机器还没有(尚未)具备预知或时间旅行(即“时间循环逻辑”)的能力,因此这些引用本质上是无法解决的。

如果将这样的级别定义为语言的强制部分,则可以说该语言是解释性的;否则,可以说它是编译型的。BASIC是解释性的,因为它的某些命令直接引用了这个层次(例如“list”命令)。同样,高级AI语言Prolog也是一个解释性语言,因为它也拥有直接引用这个层次的命令。例如,“?-”命令本身就是一个实际的提示;但它的数据库命令也涉及并维护命令级别层的当前状态。

然而,这并不排除解释性语言的某些部分受到编译或编译器使用的方法的影响,或者编译型语言在命令模式下运行。实际上,以C或C++等语言为例,调试器就是这样的。

大多数定义了命令级别层的语言,通常需要编译成某种形式。特别是,如果该语言满足以下条件,则几乎强制性地至少部分编译成某种形式:

    Does the language possess a facility for user-defined codelets,
    for instance: subroutines, functions, lambdas, etc.?

原因很简单:在定义代码之前使用它,你要把它放在哪里,以及以什么格式?直接保存和运行是极其低效的,因此通常会将其转换为另一种形式,即:(a) 语言内部的标准形式(在这种情况下,语言的其余部分可以被视为标准形式所在的减少子集语言的“语法糖”),(b) 转换成语言外部的标准形式(即“字节码”),或者(c) 两者的组合 - 它可能首先进行语言内部规范化,然后再将其转换为字节码。
因此,大多数“解释型”语言都会被编译成其他形式。唯一真正的问题是:(1) 它们被编译成什么,以及(2) 编译后的代码何时/如何运行 - 这与上述“命令级”模式的问题有关。
如果代码块被编译成与目标无关的形式 - 这通常是所谓的“字节码”时,它并不是以通常所说的意义上的“编译”方式进行的。通常情况下,“编译”一词指的是将语言翻译成运行该语言的机器本地语言。在这种情况下,将会有与语言可能运行的机器类型一样多的翻译器 - 翻译器本质上是依赖于机器的。
这两种情况并不是互斥的。因此,字节码翻译器可以出现为本地代码编译的阶段,通过这个阶段,解释性语言的代码块被直接翻译并存储到运行该语言的机器的本地语言中。这就是所谓的“即时编译”(或JIT)。
区分有点模糊。即使是编译型语言,如C或C ++,也可能在具有编译或甚至预编译的代码块的系统上运行,这些代码块在程序运行时加载。
我对JS还不够了解,无法做出任何明确的陈述 - 除了从观察中可以推断出的内容。

首先,由于JS代码存储为代码块,并且通常在需要使用时在Web客户端上运行,因此很可能会将代码块编译(或预编译)成中间字节码形式。

其次,出于安全考虑,不太可能直接编译成正在运行的机器的本地代码,因为这可能通过提供漏洞来泄露恶意代码。这就是浏览器应该遵守的“沙盒”功能。

第三,JavaScript通常不像Basic或者Prolog那样被直接用作语言。然而,在许多(甚至大多数)实现中,它确实有一个“调试”模式。例如,浏览器可能允许普通用户查看和编辑/调试JS代码。尽管如此,除了出现在Web浏览器本身中的命令层之外,实际上并没有命令层。这里未解决的问题是浏览器是否允许JS代码中的前向引用。如果是这样,那么它实际上并不是一个命令级别的环境。但这可能取决于浏览器。例如,它可能在启动任何JS代码之前加载整个网页,而不是在页面加载时尝试实时运行JS,这样前向引用就是可能的。
第四,如果该语言想要在执行速度方面高效,它将具有某种形式的JIT - 但这将需要对JS编译器本身进行严格验证,以确保没有任何东西可以通过JIT从“沙箱”溜出到主机机器上的禁止代码中。

我相信有JS编辑器/解释器存在,只是为了有一种开发JS的方式。但我不知道命令层的任何参考是否是JS规范的强制部分。如果存在这样的规范,那么我们可以称它为真正的解释语言。否则,它就像一种旨在实时运行的解释语言,但允许直接编译成正在运行的机器的本地代码,因此跨越了两种语言类型之间的边界。

对我来说,这个问题最近变得尖锐,当我试图将一个旧的基于文本的游戏(月球着陆器)直接从(解释的)FOCAL语言翻译成C-BC(一种类似于POSIX BC的扩展,其源代码位于GitHub上,链接在这里https://github.com/RockBrentwood/CBC)。C-BC和POSIX BC一样是解释性的,但允许用户定义代码块,因此BC的实现通常定义一个“字节码”语言(历史上:这是“dc”)。

FOCAL语言有一个运行时语言 - 理论上可以编译,但也有一个命令层子集(例如“库”或“擦除”命令),它不允许尚未定义的前向引用,尽管运行时语言允许前向引用。
与GNU-BC不同,C-BC具有goto语句和标签,因此可以直接翻译游戏。然而,在命令级别(在BC文件中是文件范围的顶级),这是不可能的,因为文件代码的顶级 - 就BC解释器而言 - 引用可能尚不存在的东西,因为程序也可能正在实时输入用户。相反,整个源代码必须被包含在{...}括号中 - 它首先被编译成字节码,然后在执行之前被完全编译。因此,这是一个用户定义的代码块的示例,也是大多数解释语言必须具有一些编译功能的教科书式例子。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接