一、JavaScript是解释型语言,这就是说它无需编译,直接由JavaScript引擎直接执行。

代码运行方式

对于早期熟练使用命令行的开发者来说,打开应用程序只需要键入对应程序可执行文件所在的地址:
解释型语言、编译型语言 - 图1
而在nodejs开发中执行脚本代码文件,则需要在命令行中键入:
解释型语言、编译型语言 - 图2
应用程序可执行文件可以直接打开执行,而脚本代码需要依托node平台编译执行,这就是代码的两种运行方式:直接执行和解释执行。

直接执行(AOT)

cpu提供了一套指令集,基于这套指令集可以控制整个计算机的运行,机器语言的代码就是由这些指令和对应的操作数构成的。这些机器码可以直接在计算机(机器)上运行,也就是直接执行。由它们构成的文件叫做可执行文件。
不同操作系统可执行文件的格式不同,譬如windows上是pe(Portable Executable)格式,linux系统上是elf(Excutable Linkable FOrmat)格式,macOS系统上则是mash-o格式。不同的格式规定了不同的内容应该放在文件中的什么位置,但其中真正可执行的部分还是由cpu提供的机器指令构成的机器码。
编译型语言会经过编译、汇编、链接的阶段, 其中编译是把源代码转换成汇编语言构成的中间代码,汇编是把中间代码变成目标代码,链接会把目标代码组合成可执行文件。这个可执行文件是可以在操作系统上直接执行的。因为该可执行文件由cpu的机器指令构成,因此它可以直接控制cpu。
解释型语言、编译型语言 - 图3

解释执行(JIT)

像javascript、python这类脚本语言就是解释型语言,需要依托解释器来运行。不同于编译型语言依靠生成可执行文件(机器码)在操作系统上直接执行程序,解释型语言有了解释器后不再需要生成机器码就可以在cpu上运行。其原理在于解释器是需要编译成机器码的,cpu知道如何执行解释器,解释器知道如何执行脚本代码(高级语言)。如此一来,脚本语言便依靠cpu的机器码解释执行解释器,再由解释器解释执行上层代码来完成运转。

再谈V8

chrome V8是最熟知的js解析引擎,它主要由Parse、Ignition解释器(Interpreter)、TurboFan编译器(JIT compiler)构成。大部分代码由解释器解释执行,热点代码会经过JIT编译器编译成机器码直接在OS上执行以提高性能。
解释型语言、编译型语言 - 图4
具体过程为:

  1. Parse模块会将js源码转换为AST,这是js引擎解释执行脚本代码的第一步。

如果函数只是声明没有被调用,则不会被转换为AST,V8不会去编译它。

  1. Ignition解释器将AST转换成ByteCode(字节码),同时会收集TurboFan编译器可能要优化编译所需要的信息,比如函数的类型信息。

如果函数只被调用一次,Ignition会将其编译成字节码直接执行,TurboFan编译器不会进行优化编译。这也容易理解,因为编译器需要Ignition模块收集函数执行时的类型信息,这就需要函数至少被执行1次以上,编译器才能够进行优化编译。

  1. TurboFan编译器利用Ignition模块所收集到的优化信息,将字节码转换为优化后的cpu能够直接执行的机器码,也就是指令集。

如果函数被多次调用,那么会被操作系统标记为热点函数,这个时候如果Ignition解释器收集到的信息能够证明可以做优化的话,TurboFan就会将字节码转换成为优化后的机器码交给cpu执行,以提高代码的执行性能,这部分机器码就叫做Optimized Machine Code。
但是经过TurboFan转换后的机器码也有可能被还原为字节码,比如如果函数在后续执行的过程中类型发生了变化(如fn函数原来执行的是number类型,后来执行被改变成了string类型),那么之前优化后的机器码就不能再正确执行,则会逆向还原为字节码,如上图红色箭头所示。因此在js中不应随意更改一个变量的类型。
解释型语言、编译型语言 - 图5
V8引擎处理js代码的主要流程就是这样。另外,V8中还有一个重要的模块Orinoco,用来负责垃圾回收(GC),将程序中不需要的内存空间回收释放,以减少内存负载提高性能。

解释型语言、编译型语言

一、解释型语言和编译型语言的差别:

  • 编译型语言:程序在执行之前需要一个专门的编译过程,把程序编译成为机器语言的文件(即exe文件),运行时不需要重新编译,直接用编译后的文件(exe文件)就行了。
    • 优点:执行效率高
    • 缺点:跨平台性差
  • 解释型语言:程序不需要编译,程序在运行的过程中才用解释器编译成机器语言,边编译边执行(没有exe文件)。
    • 优点:跨平台性好
    • 缺点:执行效率低

1、其中程序无需编译,不是说真的不需要编译了,直接执行脚本字符串。而是说不需要在运行之前先编译程序成为exe文件,而是在运行的过程中边运行边执行。