JavaScript引擎是使用C语言(以及少量C++)编写的高性能程序,它的核心任务就是执行JavaScript代码。

(图片来源网络,侵删)
下面我们从几个层面来深入理解这个关系。
核心关系:为什么用C语言写JavaScript引擎?
要理解这个问题,首先要明白C语言的特点和JavaScript引擎的需求。
C语言的优势:
- 性能接近硬件:C语言是“贴近系统”的语言,它允许开发者直接操作内存(指针)、管理CPU资源,这使得用C语言编写的程序运行速度极快,非常接近机器码的效率。
- 可移植性:C语言有“一次编写,到处编译”的特性,你可以将JavaScript引擎的核心代码(比如V8的源码)编译成在Windows、Linux、macOS、Android、iOS等不同操作系统上运行的二进制文件,这就是为什么同一个Node.js或Chrome浏览器可以在不同平台上运行。
- 对内存的精细控制:JavaScript引擎需要管理大量的内存,
- 垃圾回收:自动回收不再使用的JavaScript对象,这是引擎最核心、最耗时的任务之一,用C语言可以手动实现极其高效、低延迟的垃圾回收算法。
- 内存分配:为JavaScript对象在内存中分配空间。
- 成熟和稳定:C语言是系统编程的基石,拥有数十年的发展历史,工具链(编译器、调试器)非常成熟稳定,适合编写对稳定性和性能要求极高的底层系统。
JavaScript引擎的需求:
- 极致的速度:JavaScript最初是为简单脚本设计的,但如今被用于复杂的Web应用、服务器端、桌面应用等,引擎必须能以接近原生应用的速度执行代码。
- 实时性:尤其是在浏览器中,用户交互需要即时响应,长时间的“卡顿”(比如垃圾回收暂停)是不可接受的。
- 平台无关性:JavaScript本身就是一门跨平台的语言,它的执行引擎也必须是跨平台的。
C语言凭借其无与伦比的性能、对内存的控制能力和优秀的可移植性,成为了编写JavaScript引擎这种高性能、底层系统的不二之选。
JavaScript引擎是如何工作的?(以V8为例)
V8是Google开源的JavaScript引擎,被广泛用于Chrome浏览器和Node.js,我们以它为例,看看它如何将JavaScript代码变成机器指令,其中每一步都离不开C++(C语言的超集,增加了面向对象等特性)。

(图片来源网络,侵删)
V8的内部结构(简化版):
-
解析器:
- 功能:读取你的JavaScript代码,将其解析成抽象语法树。
- 实现:这部分代码是用C++编写的,它需要高效地处理文本、构建树状数据结构。
-
解释器:
- 功能:直接将AST转换成字节码,并立即执行字节码,这使得JavaScript代码可以快速运行,无需等待复杂的编译过程。
- 实现:字节码解释器是一个关键的C++程序,它在一个循环中读取每一条字节码指令,并执行对应的操作(如加法、函数调用等)。
-
分析器 & 优化编译器:
- 功能:这是V8实现“JIT(Just-In-Time,即时编译)”的关键,解释器在执行字节码的同时,会分析代码的运行模式(比如哪个函数被频繁调用,变量类型是什么等),当发现一段代码是“热点代码”(Hot Code)时,分析器会将这些信息传递给优化编译器。
- 优化编译器:它接收AST和性能分析数据,生成高度优化的机器码,如果它发现一个变量在运行时一直是数字类型,它就会生成专门处理数字的机器码,这比通用的字节码快得多。
- 实现:这部分是V8最复杂、最核心的部分,几乎完全用C++编写,因为它需要根据运行时信息动态生成和优化机器码,这对性能要求极高。
-
垃圾回收器:
(图片来源网络,侵删)- 功能:自动管理内存,找出不再使用的对象并回收其内存,防止内存泄漏。
- 实现:V8的垃圾回收器(如分代回收器、增量标记器等)是一个极其复杂的系统,用C++编写,因为它需要精确地控制内存、暂停时间,并确保在垃圾回收过程中不影响JavaScript主线程的运行。
-
接口层:
- 功能:作为JavaScript引擎和宿主环境(如Chrome的渲染引擎、Node.js的I/O系统)之间的桥梁,它允许JavaScript调用宿主环境提供的API(如
document.getElementById、fs.readFile)。 - 实现:这部分也是用C++编写的,它定义了如何将JavaScript的调用转换为对底层C++函数的调用。
- 功能:作为JavaScript引擎和宿主环境(如Chrome的渲染引擎、Node.js的I/O系统)之间的桥梁,它允许JavaScript调用宿主环境提供的API(如
一个具体的例子:2 + 3
让我们追踪一下这个简单表达式在V8中的旅程:
- 解析:V8的C++解析器将
2 + 3;解析成AST。 - 解释:V8的字节码解释器(C++程序)将AST转换成类似
ADD_CONSTANT R1, R2这样的字节码指令,并立即执行它,得到结果5。 - 优化:如果这段代码在一个循环里被执行了上千次,V8的分析器(C++程序)会注意到它是一个“热点”。
- 编译:V8的优化编译器(C++程序)会介入,它假设
2和3永远是数字类型,于是生成直接对应CPU加法指令的机器码(add eax, ebx)。 - 执行:之后,当再次执行这段代码时,V8会直接跳转到这些高度优化的机器码来执行,速度极快。
- 反优化:如果后来代码变成了
2 + "3",打破了之前的假设,V8会丢弃之前生成的机器码,回退到字节码解释器执行。
整个过程都是由C++代码驱动的。
| 特性 | C语言 | JavaScript引擎 |
|---|---|---|
| 角色 | 工具/建造材料 | 产品/建筑 |
| 关系 | JavaScript引擎是用C/C++编写的。 | C/C++是JavaScript引擎的实现语言。 |
| 目的 | 提供高性能、底层控制、跨平台能力。 | 将高级、动态的JavaScript代码翻译成机器能理解的指令并高效执行。 |
| 类比 | C语言是汽车的发动机、底盘和变速箱,决定了汽车的动力、操控和可靠性。 | JavaScript引擎是整辆汽车,它利用发动机(C代码)来实现载人、载货(执行JS代码)的功能。 |
核心要点:
- JavaScript引擎不是JavaScript解释器:它是一个极其复杂的软件系统,集了解析、解释、编译、优化、垃圾回收等多种技术。
- C/C++是实现高性能的关键:正是因为底层是用C/C++写的,JavaScript才能在性能上与原生应用一较高下,支撑起现代互联网的复杂应用。
- 跨平台的基础:用C/C++编写引擎,使得JavaScript代码可以在任何有对应编译目标平台的系统上运行,实现了“一次编写,到处运行”的愿景。
