【51CTO.com快译】朋友,您在编译和执行代码时,是否曾考虑过JavaScript背后的引擎?作为一名程序员,我建议您通过本文来了解JavaScript引擎的工作原理。这将有益于您编写出更加流畅且高质量的JavaScript代码。
什么是JavaScript引擎?
JavaScript引擎遵循的是ECMAScript标准。此类标准定义了JavaScript引擎的工作机制和所有功能。因此,JavaScript引擎是一个程序,它可以帮助您将JavaScript代码转换为较低级别的机器代码。
总的说来,诸如JavaScript和FORTRAN等高级语言都是从机器语言中抽象出来的。与C或C++相比,JavaScript的抽象程度更高。而C和C++更接近于硬件,因此运行效率也会更高一些。
编译(compilation)与解释(interpretation)
编程语言通常通过编译(compilation)和解释(interpretation)来实现代码的功能。其中,
- 编译器可以被定义为协助转换代码的程序。它可以将由任何编程语言(源语言)所编写的代码,转换为另一种目标语言的代码。例如:将源代码从高级编程语言转换为低级编程语言(即机器语言),来执行既定的任务。
- 而解释器则是通过逐行、逐条指令地分析源代码,在无需第三方参与的情况下,直接在目标机器上执行相应机器代码。
尽管编译和解释是完成编程语言的两个方面,但是由于使用解释的大多数系统都需要由编译器完成的翻译工作,因此在某些情况下两者之间是存在交集的。就JavaScript而言,其技术上的编译,也属于解释类别。也就是说,JavaScript编译器在运行的时候,其执行的就是Just-In-Time(JIT)编译。在此,JavaScript引擎恰好可以连接到浏览器和Node JS之类的Web服务器,以便用户具有运行时(run-time)编译、以及执行JavaScript代码的权限。
JavaScript引擎剖析
ECMA脚本通过指定浏览器实施JavaScript的过程,以便程序在每个单一浏览器中都能运行一致。当然,每个浏览器都为提交过的JavaScript代码提供了一个运行所需的JavaScript引擎。例如:Netscape浏览器使用的是Spider Monkey JavaScript引擎。该引擎被定义为带有零优化(zero optimizations)的基本解释器。它虽然可以运行提交过来的JavaScript代码,但是耗时较长。
工作原理
JavaScript引擎工作的基本工作流程是:获取JavaScript源代码,然后使用易于CPU理解的二进制指令(机器代码)进行编译。
JavaScript引擎主要由基线编译器组成,该编译器以中间表示(intermediate representation,IR)的形式编译代码。换言之,它被字节码所调用,然后将字节码提供给解释器。解释器使用字节代码,将代码转换为机器代码,以适合在硬件上运行。其实,这与Java的工作原理非常相似,只不过字节代码的生成是由程序员完成的,而字节代码则可以被普遍共享。
虽然基线编译器的使命是尽可能快地执行代码编译,但是它还是会生成未经优化的字节码。在解释器中,这些字节码会导致应用程序的速度变慢。为此,Mozilla Firefox浏览器将Spider Monkey JavaScript进行了嵌入,以优化并提高机器代码的效率。例如:JavaScript引擎会直观地了解到变量的数据类型,进而更好地生成少量的机器代码。
此外,JavaScript引擎还可以根据代码的执行情况,通过收集和分析数据,发现代码运行缓慢的原因,进而给出优化并替换的建议。
知名的JavaScript引擎
除了前面介绍过Firefox正在使用的Spider Monkey引擎,还有Internet Explorer正在使用的Chakra引擎,而Google使用的是V8引擎。虽然它们用到了不同的编译器,但是它们所遵循的优化结构是相似的。
Google的V8:
由Lark Bak创建的The Chromium项目开发了开源的Javascript引擎。该项目为Google Chrome和Chromium网络浏览器提供了开发服务。Chrome的V8引擎几乎是在Chrome的首个版本诞生(2008年9月2日)的同时,就应运而生了。V8能够很好地兼容Node.js和MongoDB等服务器端技术。
由于V8带有Ignition解释器,因此它能够通过低级字节码完成解释与执行。尽管这些低级字节码比起机器码来说既小又慢,但它们需要编译的时间会更少。我们可以使用Full-Codegen编译器,来生成未优化的代码。该编译器的运行速度比其他编译器要快一些。JIT编译器—Turbofan不但可以编译代码,并且能够密切注意代码是否会在整个JavaScript执行的过程中被多次用到。也就是说,它的垃圾收集器会观察各种对象中不再引用到的数据。而且这种数据的收集工作是由收集器来完成的。值得一提的是,在执行垃圾回收的周期内,V8引擎将会自动停止程序的运行。
CHAKRA:
由Microsoft开发的Chakra JavaScript引擎,被用在了Microsoft Edge Web浏览器中。它是Internet Explorer中Jscript引擎的一个分支。我们可以简单地理解为:Chakra提高了Internet Explorer中JavaScript的执行质量。由于是用新的JavaScript编译器组成,因此Chakra可以帮助用户将JavaScript代码编译成为高级机器代码。通过提供全新的解释器,Chakra不但能够在传统的网页上执行脚本,而且能够改进JavaScript的运行时和各种库。
SPIDER MONKEY:
由Netscape Communications的Brendan Eich使用C和C++语言编写的Spider Monkey,最终被发布成为了开源的JavaScript引擎。目前,它被用在包括Firefox在内的许多享有Mozilla公共许可证(2.0版)的产品中。它将类型推断(type inference)与JIT编译器—Jaegermonkey相连接,以生成有效的代码。
在结构上,Spider Monkey是由一个解释器、几个JIT编译器、一个反编译器、以及一个垃圾收集器所组成。
RHINO:
由Mozilla Foundation管理的Rhino JavaScript引擎,是一款完全由Java编写的开源软件。它同样可以被使用在Mozilla Firefox中。
JavaScript运行时
与其他编程语言不同的是,JavaScript是一种单线程的语言运行时(language runtime),且只能一次性执行并完成程序代码。由于代码是按照顺序被执行的,因此那些需要花费较长时间的代码,可能会阻塞后续有待执行的其他代码。例如:当您在浏览器(如:Google Chrome)上打开某个网站时,它会调用JavaScript的一个执行线程,用来处理诸如滚动网页,在网页上打印部分内容,侦听DOM事件,以及执行某项操作等。然而,如果JavaScript一旦停止执行,浏览器就会自动停止所有各项操作。这就意味着浏览器在完成某项任务之前,并不会响应其他的任何内容。
根据概念,JavaScript运行时是指JavaScript代码所处的环境或条件。因此,当JavaScript在Google Chrome上执行时,JavaScript运行时便是v8;如果在Mozilla上,它就是Spider Monkey;如果在IE上,则为Chakra。
JavaScript运行时的API提供了一种执行桌面与服务器端应用的方法。由于这些API在Windows 10和任何版本的Windows操作系统上可用,因此Windows操作系统可以通过使用Chakra的相关标准,向应用程序添加不同的脚本功能。当然,目标系统上也需要安装好Internet Explorer 11。
内联缓存(Inline Caching)
内联缓存的概念源于经验观察。也就是说,通过观察并记住以前直接在调用站点处那些相同方法的查询结果,来加快运行时方法的绑定速度,并提高查找的性能。同时,为了简化此过程,我们可以为调用站点分配不同的状态。例如:将最初的站点分配为“未初始化(uninitialized)”状态。那么,语言运行时到达未初始化的特定调用站点时,将执行动态查找,并将结果存储到调用站点中,同时将其状态更改为“单态(monomorphic)”。后续,如果语言运行时再次到达同一调用站点时,它会从现有的存储中进行检索,并直接予以调用,而无需再次执行任何其他的查找。此外,为了解决不同类型的对象可能出现在同一调用站点的可能性,语言运行时还必须在代码中插入保护条件(guard conditions)。
至此,希望您已经对JavaScript引擎的相关概念和工作原理有所了解。希望您能更好地将其运用到项目编程中。
原文How JavaScript Engine Works?,作者:Vyom Srivastava
【51CTO译稿,合作站点转载请注明原文译者和出处为51CTO.com】