文章目录
JS代码执行过程
JavaScript 代码的执行过程是一个复杂且细致的过程,涉及多个阶段
和内部机制
。
1 - 词法分析(Lexical Analysis)
首先,JavaScript 引擎会将源代码(文本)转化为一系列的词法单元(tokens)
。
这个过程叫做词法分析或扫描(Lexical Analysis or Scanning)
。
每个词法单元都是源代码中的一个原子元素
,比如变量名
、操作符
或数字字面量
等。
代码 -> 词法单元(tokens)
2 - 语法分析(Syntax Analysis)
接下来,引擎会将词法单元
转化为一个抽象语法树(Abstract Syntax Tree, AST)。
这个过程叫做语法分析
或解析(Syntax Analysis or Parsing)
。
AST 是源代码的树形表现形式,它更易于 JavaScript 引擎进行后续的操作和优化。
代码 -> 词法单元(tokens) -> AST(抽象语法树)
3 - 语义化(Semanticization)
在生成 AST 之后,引擎会进行语义化过程
,检查源代码是否有语义错误。
例如,检查变量是否在使用前已经声明
,函数参数是否匹配
等。如果有错误,引擎会抛出异常。
代码 -> 词法单元(tokens) -> AST(抽象语法树)-> 语义化检查
4 - 代码生成
然后,引擎会根据 AST 生成可执行代码。
这个过程通常涉及将 AST 转化为字节码(bytecode)
或其他中间表示形式(Intermediate Representation, IR)
,以便后续的优化和执行。
代码 -> 词法单元(tokens) -> AST(抽象语法树)-> 语义化检查 -> bytecode或IR
5 - 执行
最后,引擎会执行生成的代码。
这通常涉及一个叫做“执行上下文”(Execution Context)的概念。
每个执行上下文
都有一个与之关联的变量对象(Variable Object)
,它存储了
在当前执行上下文中定义的所有变量和函数
。
执行过程通常按照以下步骤进行:
- 创建全局执行上下文:当 JavaScript 代码开始执行时,会首先创建一个全局执行上下文。这个上下文包含了全局对象(在浏览器中通常是
window
对象)以及所有在全局作用域中定义的变量和函数。 - 函数执行上下文:当调用一个函数时,会创建一个新的函数执行上下文,并将其推入执行上下文栈(Execution Context Stack)。这个新的上下文包含了函数的参数、局部变量以及
this
和arguments
对象。 - 代码执行:在当前的执行上下文中,引擎会按照源代码的顺序逐行执行代码。这包括变量的赋值、函数的调用、控制结构的执行(如
if
语句和循环)等。 - 上下文切换:当函数调用发生时,新的函数执行上下文会被推入执行上下文栈,并成为当前的执行上下文。当函数执行完毕后,它的执行上下文会从栈中弹出,之前的执行上下文重新成为当前上下文。这个过程会一直持续到整个代码执行完毕。
执行上下文栈
全局执行上下文
函数执行上下文
异步执行上下文
6 - 注意事项
-
异步执行:JavaScript 的事件循环(Event Loop)机制允许异步代码(如 setTimeout、Promise、async/await 等)在将来某个时间点执行,而不会阻塞主线程。
- 这意味着异步代码的执行时机与同步代码不同,它们可能在
不同的执行上下文
中执行。
- 这意味着异步代码的执行时机与同步代码不同,它们可能在
-
优化:现代 JavaScript 引擎(如
V8
、SpiderMonkey
等)使用了许多复杂的优化技术来提高代码的执行效率。- 这些优化可能包括即时编译(JIT Compilation)、==内联缓存(Inline Caching)和隐藏类(Hidden Classes)==等。
- 这些优化过程通常发生在
代码生成和执行阶段
。
总之,JavaScript 代码的执行过程是一个复杂且多阶段的过程,涉及词法分析、语法分析、语义化、代码生成和执行等多个步骤。了解这个过程有助于我们更好地理解和优化 JavaScript 代码的性能。
V8引擎
V8引擎的名字来源于汽车的“V型8缸发动机”(V8发动机)。
V8发动机以其强大的动力和高效的性能而闻名,V8引擎的命名旨在表示这是一款强力且高速的JavaScript引擎。
在2008年,V8引擎最初由Lars Bak团队
开发,并与Chrome浏览器一同开源发布。
其设计初衷是为了提供一款高性能的JavaScript执行环境,以满足现代Web应用程序的复杂需求。
通过借鉴V8发动机的概念,V8引擎在JavaScript执行速度和效率方面取得了显著的提升,成为了现代Web开发中不可或缺的一部分。
一种比V型8缸发动机性能更加强劲的发动机类型是V12发动机。
V12发动机是指布局为V型的12缸发动机
,其气缸排列在发动机机体里的方式
经过特殊设计,可以呈现出更优秀的发动机特性。
此外,北美公司发明的一款名为Hurricane
的3.0升、直列、6缸、双涡轮、增压发动机,在性能上也超越了目前市面上最高规格的同级发动机。
Hurricane主要用来描述飓风或暴风。这个词源自印第安语言,原词是“hurricane”。
读音是:哈瑞肯
该发动机具有两种不同的输出模式,标准版本提供超过400的最大马力
和450牛米的扭矩
,而高输出版本更是超过500马力
、644牛·米
。
该发动机还采用了铝制缸体和缸盖
、等离子、转移线、电弧工艺、喷涂钻孔、的气缸
、薄铁涂层
等技术,使其更加耐用。
同时,它还应用了双顶置凸轮轴
、直喷
等技术,燃油效率相比V8发动机提高了15%。
V8工作原理
V8引擎是Google开发的一款高性能JavaScript和WebAssembly引擎,它用于Chrome浏览器和Node.js等环境中。
V8引擎的工作原理可以概括为几个关键步骤:解析源代码
、生成抽象语法树(AST)
、编译成机器码
、执行代码
以及内存管理
。
-
解析源代码:
- V8引擎首先将JavaScript源代码作为输入。
- ==解析器(Parser)==负责将源代码转换为抽象语法树(AST)。这个过程包括词法分析和语法分析。词法分析将源代码分解为标记(tokens),而语法分析则将这些标记组合成AST。AST是源代码的树形表示,它反映了代码的结构和语法。
-
生成抽象语法树(AST):
- AST是代码的结构化表示,包含了代码的所有信息,例如变量声明、函数调用、控制结构等。
- AST是后续编译步骤的基础,它使得V8引擎能够理解和处理JavaScript代码。
-
编译成机器码:
- V8引擎使用两种主要的编译技术:解释执行和即时编译(JIT)。
- 解释执行:V8引擎包含一个解释器(如Ignition),它可以直接解释AST并执行。解释执行在代码首次运行时发生,它的速度相对较慢,但能够快速地开始执行代码。
- 解释器会按照AST中的指令顺序,逐个执行节点所代表的操作。
- 解释器(如Ignition)将AST(抽象语法树)解释成为一种特定格式的字节码(bytecode)
- 字节码是介于AST和机器码之间的一种中间表示形式。
- 即时编译(JIT):为了提高执行效率,V8引擎使用==JIT编译器(如TurboFan)将热点代码(频繁执行的代码)==编译成高效的机器码。JIT编译在代码运行时进行,并且会根据代码的运行特性进行优化。这种编译方式可以显著提高代码的执行速度。
-
执行代码:
- 编译后的机器码由V8引擎的执行引擎执行。执行引擎负责
管理调用栈、处理函数的调用和返回、以及执行控制结构
等操作。 - 在执行过程中,V8引擎还会进行各种优化,如
内联缓存
和隐藏类
的使用,以提高代码的执行效率。
- 编译后的机器码由V8引擎的执行引擎执行。执行引擎负责
-
内存管理:
- V8引擎使用垃圾回收机制来管理内存,确保不再使用的对象能够及时被回收,避免内存泄漏。
- V8引擎的
垃圾回收器(如Orinoco)
会定期扫描内存中的对象,并标记出仍然被引用的对象。未被标记的对象则被认为是垃圾,并被回收。
除了上述主要步骤外,V8引擎还进行了一些其他的优化工作,如隐藏类的使用。
隐藏类是在对象创建时自动生成的,它记录了对象的属性和方法。
通过使用隐藏类,V8引擎可以更快地访问对象的属性,提高了代码的执行效率。
解析器(Parser)
解释器(如Ignition)
JIT编译器(如TurboFan)
执行引擎
垃圾回收器(如Orinoco)
V8引擎的内存管理模型
V8引擎中的内存管理模型是一个复杂但高效的体系,它旨在优化内存使用并提高程序的执行性能。以下是V8引擎中内存分区的主要管理模型:
- 常驻内存:Node程序运行中,进程占用的所有内存称为
常驻内存(Resident Set)
。这包括代码区、栈、堆
以及堆外内存
等部分。
- 代码区:存放即将执行的代码片段。
- 栈:存放
局部变量
。栈的分配与回收非常直接,当程序离开某作用域后,其栈指针下移(回退)
,整个作用域的局部变量都会出栈,内存收回。 - 堆:存放
对象、闭包上下文
。这是V8引擎内存管理的核心部分,它采用了分代式垃圾回收机制
,将内存(堆)分为新生代和老生代两部分。 - 堆外内存:不通过V8分配,也不受V8管理。例如,Buffer对象的数据就存放于此。
- 新生代与老生代:
- 新生代:对象一般存活时间较短,使用
Scavenge GC算法
。- 在新生代中,对象分配在一块称为From空间的内存区域中,当From空间满后,存活的对象会被复制到To空间中,并将From空间清空。
- 然后,From和To空间的角色互换,继续这个过程。
- 老生代:对象分配在一块称为Oldspace的内存区域中。
- V8在此区域采用了
标记-清除(Mark-Sweep)
和标记-整理(Mark-Compact)
两种垃圾回收算法。 标记-清除算法
会遍历堆中所有的对象,标记所有存活的对象,并清除未标记的对象。标记-整理算法
则进一步整理内存空间,以减少内存碎片。
- V8在此区域采用了
- 垃圾回收与优化:V8引擎的垃圾回收机制旨在确保及时回收不再使用的内存,避免内存泄漏。
- 需要注意的是,V8引擎还设置了内存使用限制。
- 例如,
- 在64位系统下,V8引擎限制了所能使用的内存极限为1.4GB,
- 而在32位系统下则为1.0GB。
- 当申请的堆空间超过这个限制时,程序可能会崩溃。
- 例如,