前言
作为一名编程小白,想要学会一门语言,我们首先得了解一下这门语言的编译原理!
所以我们今天来学习一下学习JavaScript编译原理!
正文
虽然,我们通常把JavaScript语言归类于"动态"或者"解释执行"语言,事实上,JavaScrip是一门"解释执行"的语言,它的编译其实发生在执行之前的瞬间(这个过程或许只需要几毫秒),其中有些环境十分繁琐,我们就来介绍一下,JavaScript编译的三部曲!分词/词法分析、解析/语法分析和代码生成。
分词/词法分析
分词/词法分析这个过程会将我们由字符组成的字符串分解为一个又一个的有意义的代码块!这些代码块又被我们称为:词法单元(token)
JavaScript中的分词是将源代码字符串分解成一个个独立的词法单元的过程。这些词法单元是编程语言中的基本元素,如变量名、关键字、运算符等。分词是编译器的第一个步骤,它为后续的解析和生成可执行代码奠定了基础。
在JavaScript中,分词的主要目的是将源代码转换为更小的词法单元序列,以便于解析器进行语法分析。词法单元是构成源代码的基本元素,它们组合在一起构成了程序的语法结构。
JavaScript的分词算法通常采用基于正则表达式的匹配方法。在分词过程中,编译器会根据预先定义的正则表达式来匹配源代码中的字符序列,并尽可能地将其分解为独立的词法单元。这些正则表达式通常对应于JavaScript语言中的各种语法规则。
正则表达式部分知识学习大家可以参考:[小白篇]字符串"克星"--正则表达式 - 掘金 (juejin.cn)
例如:var num = 10;
这一句代码块,我们的程序会把它分解成这些词法单元:var
,num
,=
,10
,;
。空格是否会被当作词法单元,取决于空格这个字符对我们的编译语言来说是否具有意义,如果没有意义的话,我们的程序会忽视空格的存在,如果有意义,我们的程序也会为其单独分配出一个独立的词法单元!
根据我们书籍的介绍:
分词(tokenizing)和词法分析(Lexing)之间的区别是非常微妙,晦涩的,主要差异在于词法单元的识别是通过有状态还是无状态的方式进行的。简单来说,如果词法单元生成器在判断a是一个独立的词法单元还是其它词法单元的一部分时,调用的是有状态的解析规则,那么这个过程就被称为词法分析
总之,JavaScript中的分词是将源代码分解为一个个独立的词法单元的过程,这些词法单元对应于编程语言中的基本元素。分词是编译器的重要组成部分,它为后续的解析和生成可执行代码奠定了基础。
解析/语法分析(Parsing)
在JavaScript编译过程中,语法分析是第二个阶段,它建立在词法分析的基础之上。语法分析的目的是将词法单元序列转化为抽象语法树(AST, Abstract Syntax Tree)。
抽象语法树是一种以树形结构表示源代码语法结构的特殊数据结构。它由节点和树节点组成,其中节点代表源代码中的各种语法元素,如变量声明、函数声明、表达式等,而树节点则是这些元素的嵌套组合。换而言之:抽象语法树是一个由元素逐级嵌套组成的代表了程序语法结构的树。
在语法分析阶段,编译器会根据JavaScript语言的语法规则,将词法单元序列转化为AST。这个过程涉及到对源代码的深度优先遍历和节点的递归组合。
例如,对于以下JavaScript代码:
javascript
var a = 2;
var b = 3;
var c = a + b;
编译器首先会进行词法分析,将其分解为一系列的词法单元,如"var"、"a"、"="、"2"、"var"、"b"、"="、"3"、"var"、"c"、"="、"a"、"+"、"b"等。然后,编译器会根据JavaScript语言的语法规则,将这些词法单元转化为AST。
在这个例子中,AST将包括三个节点,分别是变量声明节点 、赋值表达式节点 和加法表达式节点。其中,变量声明节点包括变量名"a"和赋值的值"2",赋值表达式节点包括变量名"b"和赋值的值"3",加法表达式节点包括变量名"a"和"b"的值,并将结果赋值给变量"c"。
总之,JavaScript编译中的语法分析阶段将源代码转化为抽象语法树(AST),这个过程涉及到对源代码的深度优先遍历和节点的递归组合。AST是编译器进行代码生成的重要依据,它为后续的代码生成提供了基础。
代码生成
在JavaScript编译过程中,代码生成是最后一个阶段,它建立在语法分析的基础之上。代码生成的目的是将AST转换为可执行的机器指令。
在代码生成阶段,JavaScript引擎会根据AST的结构和内容,生成对应于每个节点的可执行代码。这个过程通常包括遍历AST并对其中的每个节点进行相应的处理,以生成最终的机器指令。
具体而言,代码生成通常包括以下步骤:
- 遍历AST,对每个节点进行相应的处理。
- 根据节点的类型和属性,生成对应的机器指令或函数调用。
- 将生成的机器指令或函数调用序列组合成最终的可执行代码。
在代码生成阶段,编译器还需要对源代码进行大量的优化操作,以提高生成代码的性能和效率。这些优化包括常量折叠、变量名替换、函数内联等。
例如,对于以下JavaScript代码:
javascript
var a = 2;
var b = 3;
var c = a + b;
在代码生成阶段,编译器会根据AST生成对应的机器指令。对于变量声明节点,编译器会生成对应的变量创建和赋值操作;对于加法表达式节点,编译器会生成对应的加法操作指令。这些指令会被组合成最终的可执行代码,以实现源代码的功能。
简单理解,就是通过某种方法将例如:var num = 10
的AST转化为一组机器指令,用来创建一个叫做num
的变量(包括分配内存等),并将一个值存储在num
当中!
总之,JavaScript编译中的代码生成阶段将AST转化为可执行的机器指令或函数调用,这个过程涉及到遍历AST、生成对应的机器指令或函数调用,以及进行大量的优化操作。最终生成的代码可以在计算机上执行,并实现源代码的功能。
总结
JavaScript的编译原理包括词法分析、语法分析和代码生成三个主要步骤。
- 词法分析:将源代码字符串分解为单个的单词或标记,这些标记包括关键字、变量名、运算符和常量。这个过程通常由词法分析器完成,它以源代码作为输入,并输出一个由标记组成的列表。
- 语法分析:将词法分析阶段输出的标记转换为语法树。语法树是一种树形结构,可以表示代码的结构和语法。这个过程通常由语法分析器完成,它根据语言的语法规则对标记进行解析,并构建一个表示代码结构的语法树。
- 代码生成:将语法树转换为可执行的机器代码。这个过程包括遍历语法树,并为每个节点生成相应的机器指令或函数调用。生成的指令可以组成一个可执行的程序,可以在计算机上运行并实现源代码的功能。
好啦!我们今天有关JavaScript的编译原理就学习到这里啦!
有任何想法和建议欢迎大家在评论区留言哦~
点个赞鼓励支持一下吧!🌹🌹🌹
参考文献:《你不知道的JavaScript 上卷》