前言
Babel为当前最流行的代码JavaScript编译器了,其使用的JavaScript解析器为babel-parser,最初是从Acorn
项目fork
出来的。Acorn 非常快,易于使用,并且针对非标准特性(以及那些未来的标准特性) 设计了一个基于插件的架构。本文主要介绍esprima
解析生成的抽象语法树节点,esprima
的实现也是基于Acorn的。
解析器 Parser
JavaScript Parser 是把js源码转化为抽象语法树(AST)的解析器。这个步骤分为两个阶段:词法分析(Lexical Analysis) 和 语法分析(Syntactic Analysis)。
常用的JavaScript Parser:
词法分析
词法分析阶段把字符串形式的代码转换为 令牌(tokens)流。你可以把令牌看作是一个扁平的语法片段数组。
1 | n * n; |
例如上面n*n
的词法分析得到结果如下:
1 | [ |
每一个 type 有一组属性来描述该令牌:
1 | { |
和 AST 节点一样它们也有 start,end,loc 属性。
语法分析
语法分析就是根据词法分析的结果,也就是令牌tokens,将其转换成AST。
1 | function square(n) { |
如上面代码,生成的AST结构如下:
1 | { |
下文将对AST各个类型节点做解释。更多AST生成,入口如下:
结合可视化工具,举个例子
如下代码:
1 | var a = 42; |
第一步词法分析之后长成如下图所示:
语法分析,生产抽象语法树,生成的抽象语法树如下图所示
Base
Node
所有节点类型都实现以下接口:
1 | interface Node { |
该type字段是表示AST变体类型的字符串。该loc字段表示节点的源位置信息。如果解析器没有生成有关节点源位置的信息,则该字段为null;否则它是一个对象,包括一个起始位置(被解析的源区域的第一个字符的位置)和一个结束位置.
1 | interface SourceLocation { |
每个Position对象由一个line数字(1索引)和一个column数字(0索引)组成:
1 | interface Position { |
Programs
1 | interface Program <: Node { |
表示一个完整的源代码树。
Scripts and Modules
源代码数的来源包括两种,一种是script脚本,一种是modules模块
当为script时,body为StatementListItem
。
当为modules时,body为ModuleItem
。
类型StatementListItem
和ModuleItem
类型如下。
1 | type StatementListItem = Declaration | Statement; |
ImportDeclaration
import语法,导入模块
1 | type ImportDeclaration { |
ImportSpecifier
类型如下:
1 | interface ImportSpecifier { |
ImportSpecifier
语法如下:
1 | import { foo } from './foo'; |
ImportDefaultSpecifier
语法如下:
1 | import foo from './foo'; |
ImportNamespaceSpecifier
语法如下
1 | import * as foo from './foo'; |
ExportDeclaration
export类型如下
1 | type ExportDeclaration = ExportAllDeclaration | ExportDefaultDeclaration | ExportNamedDeclaration; |
ExportAllDeclaration
从指定模块中导出
1 | interface ExportAllDeclaration { |
语法如下:
1 | export * from './foo'; |
ExportDefaultDeclaration
导出默认模块
1 | interface ExportDefaultDeclaration { |
语法如下:
1 | export default 'foo'; |
ExportNamedDeclaration
导出部分模块
1 | interface ExportNamedDeclaration { |
语法如下:
1 | export const foo = 'foo'; |
Declarations and Statements
declaration
,即声明,类型如下:
1 | type Declaration = VariableDeclaration | FunctionDeclaration | ClassDeclaration; |
statements
,即语句,类型如下:
1 | type Statement = BlockStatement | BreakStatement | ContinueStatement | |
VariableDeclarator
变量声明,kind 属性表示是什么类型的声明,因为 ES6 引入了 const/let。
1 | interface VariableDeclaration <: Declaration { |
FunctionDeclaration
函数声明(非函数表达式)
1 | interface FunctionDeclaration { |
例如:
1 | function foo() {} |
ClassDeclaration
类声明(非类表达式)
1 | interface ClassDeclaration { |
ClassBody
声明如下:
1 | interface ClassBody { |
MethodDefinition
表示方法声明;
1 | interface MethodDefinition { |
1 | class foo { |
ContinueStatement
continue语句
1 | interface ContinueStatement { |
例如:
1 | for (var i = 0; i < 10; i++) { |
DebuggerStatement
debugger语句
1 | interface DebuggerStatement { |
例如
1 | while(true) { |
DoWhileStatement
do-while语句
1 | interface DoWhileStatement { |
test
表示while条件
例如:
1 | var i = 0; |
EmptyStatement
空语句
1 | interface EmptyStatement { |
例如:
1 | if(true); |
ExpressionStatement
表达式语句,即,由单个表达式组成的语句。
1 | interface ExpressionStatement { |
当表达式语句表示一个指令(例如“use strict”)时,directive属性将包含该指令字符串。
例如:
1 | (function(){}); |
ForStatement
for语句
1 | interface ForStatement { |
ForInStatement
for…in语句
1 | interface ForInStatement { |
ForOfStatement
for…of语句
1 | interface ForOfStatement { |
IfStatement
if 语句
1 | interface IfStatement { |
consequent
表示if命中后内容,alternate
表示else或者else if的内容。
LabeledStatement
label语句,多用于精确的使用嵌套循环中的continue和break。
1 | interface LabeledStatement { |
如:
1 | var num = 0; |
ReturnStatement
return 语句
1 | interface ReturnStatement { |
SwitchStatement
Switch语句
1 | interface SwitchStatement { |
discriminant
表示switch的变量。
SwitchCase
类型如下
1 | interface SwitchCase { |
ThrowStatement
throw语句
1 | interface ThrowStatement { |
TryStatement
try…catch语句
1 | interface TryStatement { |
handler
为catch处理声明内容,finalizer
为finally内容。
CatchClaus
类型如下
1 | interface CatchClause { |
例如:
1 | try { |
WhileStatement
while语句
1 | interface WhileStatement { |
test
为判定表达式
WithStatement
with语句(指定块语句的作用域)
1 | interface WithStatement { |
如:
1 | var a = {}; |
Expressions and Patterns
Expressions
可用类型如下:
1 | type Expression = ThisExpression | Identifier | Literal | |
Patterns
可用有两种类型,函数模式和对象模式如下:
1 | type BindingPattern = ArrayPattern | ObjectPattern; |
ThisExpression
this
表达式
1 | interface ThisExpression { |
Identifier
标识符,就是我们写 JS 时自定义的名称,如变量名,函数名,属性名,都归为标识符。相应的接口是这样的:
1 | interface Identifier { |
Literal
字面量,这里不是指 [] 或者 {} 这些,而是本身语义就代表了一个值的字面量,如 1,“hello”, true 这些,还有正则表达式(有一个扩展的 Node 来表示正则表达式),如 /\d?/。
1 | interface Literal { |
例如:
1 | var a = 1; |
ArrayExpression
数组表达式
1 | interface ArrayExpression { |
例:
1 | [1, 2, 3, 4]; |
ArrayExpressionElement
数组表达式的节点,类型如下
1 | type ArrayExpressionElement = Expression | SpreadElement; |
Expression包含所有表达式,SpreadElement为扩展运算符语法。
SpreadElement
扩展运算符
1 | interface SpreadElement { |
如:
1 | var a = [3, 4]; |
ObjectExpression
对象表达式
1 | interface ObjectExpression { |
Property
代表为对象的属性描述
类型如下
1 | interface Property { |
kind
用来表示是普通的初始化,或者是 get/set。
例如:
1 | var obj = { |
FunctionExpression
函数表达式
1 | interface FunctionExpression { |
例如:
1 | var foo = function () {} |
ArrowFunctionExpression
箭头函数表达式
1 | interface ArrowFunctionExpression { |
generator
表示是否为generator函数,async
表示是否为async/await函数,params
为参数定义。
FunctionParameter
类型如下
1 | type FunctionParameter = AssignmentPattern | Identifier | BindingPattern; |
例:
1 | var foo = () => {}; |
ClassExpression
类表达式
1 | interface ClassExpression { |
例如:
1 | var foo = class { |
TaggedTemplateExpression
标记模板文字函数
1 | interface TaggedTemplateExpression { |
TemplateLiteral
类型如下
1 | interface TemplateLiteral { |
TemplateElement
类型如下
1 | interface TemplateElement { |
例如
1 | var foo = function(a){ console.log(a); } |
MemberExpression
属性成员表达式
1 | interface MemberExpression { |
例如:
1 | const foo = {bar: 'bar'}; |
Super
父类关键字
1 | interface Super { |
例如:
1 | class foo {}; |
MetaProperty
(这个不知道干嘛用的)
1 | interface MetaProperty { |
例如:
1 | new.target // 通过new 声明的对象,new.target会存在 |
CallExpression
函数执行表达式
1 | interface CallExpression { |
Import类型,没搞懂。
1 | interface Import { |
ArgumentListElement
类型
1 | type ArgumentListElement = Expression | SpreadElement; |
如:
1 | var foo = function (){}; |
NewExpression
new 表达式
1 | interface NewExpression { |
UpdateExpression
更新操作符表达式,如++
、--
;
1 | interface UpdateExpression { |
如:
1 | var i = 0; |
AwaitExpression
await表达式,会与async连用。
1 | interface AwaitExpression { |
如
1 | async function foo() { |
UnaryExpression
一元操作符表达式
1 | interface UnaryExpression { |
枚举UnaryOperator
1 | enum UnaryOperator { |
BinaryExpression
二元操作符表达式
1 | interface BinaryExpression { |
枚举BinaryOperator
1 | enum BinaryOperator { |
LogicalExpression
逻辑运算符表达式
1 | interface LogicalExpression { |
如:
1 | var a = '-'; |
ConditionalExpression
条件运算符
1 | interface ConditionalExpression { |
例如:
1 | var a = true; |
YieldExpression
yield表达式
1 | interface YieldExpression { |
例如:
1 | function* gen(x) { |
AssignmentExpression
赋值表达式。
1 | interface AssignmentExpression { |
operator
属性表示一个赋值运算符,left
和right
是赋值运算符左右的表达式。
SequenceExpression
序列表达式(使用逗号)。
1 | interface SequenceExpression { |
1 | var a, b; |
ArrayPattern
数组解析模式
1 | interface ArrayPattern { |
例:
1 | const [a, b] = [1,3]; |
elements代表数组节点
ArrayPatternElement如下
1 | type ArrayPatternElement = AssignmentPattern | Identifier | BindingPattern | RestElement | null; |
AssignmentPattern
默认赋值模式,数组解析、对象解析、函数参数默认值使用。
1 | interface AssignmentPattern { |
例:
1 | const [a, b = 4] = [1,3]; |
RestElement
剩余参数模式,语法与扩展运算符相近。
1 | interface RestElement { |
例:
1 | const [a, b, ...c] = [1, 2, 3, 4]; |
ObjectPatterns
对象解析模式
1 | interface ObjectPattern { |
例:
1 | const object = {a: 1, b: 2}; |
结束
AST的作用大致分为几类
IDE使用,如代码风格检测(eslint等)、代码的格式化,代码高亮,代码错误等等
代码的混淆压缩
转换代码的工具。如webpack,rollup,各种代码规范之间的转换,ts,jsx等转换为原生js
了解AST,最终还是为了让我们了解我们使用的工具,当然也让我们更了解JavaScript,更靠近JavaScript。