携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第2天,点击查看活动详情
引言:你知道JavaScript源代码是如何运行的吗?其运行时的环境又是由什么构成的?
目的:那么接下来让我们一起去了解JavaScript源代码的运行过程以及其运行时的环境构成。
1. 导读
我们知道高级的编程语言都是需要转成最终的机器指令来执行的; 事实上我们编写的JavaScript无论你交给浏览器或者Node执行,最后都是需要被CPU执行的;
但是CPU只认识自己的指令集,实际上是机器语言,才能被CPU所执行; 所以我们需要一个工具来帮助我们将JavaScript代码编译成CPU指令,而这一工具恰好是JavaScript引擎。
2. JavaScript引擎是什么
JavaScript引擎是一种为解释和执行JavaScript代码设计的虚拟机,其一般包括以下几个部分:
- 解析器,主要将源代码编译成抽象语法树(Abstract Syntax Tree,简称AST);
- 解释器,主要将AST转化为字节码并执行,同时会标记热点代码;
- 编译器,主要将热点代码编译成机器码并执行;
- 垃圾回收器,主要将无用的内存空间进行回收。
3. 常见的JavaScript引擎有那些
那么,我们常见的浏览器其所对应的JavaScript引擎是哪些呢?
| 浏览器 | 引擎 | 公司 |
|---|---|---|
| Firefox | TraceMonkey | Mozilla |
| Edge | ChakraCore | Microsoft |
| IE | JScript | Microsoft |
| Safari | SquirrelFish | Apple |
| Chrome | V8 |
4. V8引擎编译过程
下面重点对Google的V8引擎的编译过程进行介绍。
4.1. 早期V8架构
早期的 V8 引擎有 Full-Codegen 和 Crankshaft 两个编译器,V8 首先使用 Full-Codegen 把所有的代码都编译一次,生成对应的机器码。JS 在执行过程中,V8 内置的 Profiler 筛选出热点函数,然后交给 Crankshaft 来进行优化。所以Full-Codegen本质上生成的是未优化的机器码,而Crankshaft生成的是优化过的机器码。
4.2. 早期V8架构的缺陷
- Full-Codegen编译直接生成机器码,导致内存占用大;
- Full-Codegen编译直接生成机器码,导致编译时间长,启动速度慢;
- Crankshaft无法优化try,catch和finally等关键词划分的代码快;
- Crankshaft新加语法支持,需要为此编写适配不同的Cpu架构代码。
4.3.现有V8架构
现有的V8引擎只有Turbofan编译器。V8通过Ignition解释器将AST转化为字节码并执行。JS在执行的过程中,V8 内置的 Profiler 筛选出热点函数,然后交给 Turbofan编译器来进行优化,并生成机器码。所以Ignition生成的是字节码,而Turbofan生成的是优化过的机器码。
5. JavaScript运行时环境
JavaScript运行时,全称JavaScript Runtime Environment。这个环境可以看出是一个大的容器,提供了代码在执行时能够利用的附加特性。由于 JavaScript既可以在浏览器中运行,也可以在Node中运行,因此其运行时环境分别有两种。
5.1 浏览器环境
在浏览器中的运行时环境主要由三部分组成:
- JavaScript引擎:负责解释、执行源代码,包含内存堆和调用栈;
- Web APIs:web API(document、XMLHttpRequest、setTimeout),由浏览器提供;
- Callback Queue:是一个线程池,负责除主线程之外的线程的调度;
5.2 Node环境
在Node中的运行时环境也主要由三部分组成,与浏览器中的运行时环境的唯一区别就是Web APIs换成了Node APIs:
- JavaScript引擎:负责解释、执行源代码,包含内存堆和调用栈;
- Node APIs:Node API(path、fs),由Node提供;
- Callback Queue:是一个线程池,负责除主线程之外的线程的调度;