什么是AST?
AST译名抽象语法树(Abstract Syntax Tree),是一种用于表示源代码结构的数据结构。
它在编译器、解析器和静态代码分析等领域中被广泛使用。
AST结构分析
我们利用成熟的astexplorer来进行结构化的比较和分析。可以尝试登录以下网址访问:
https://astexplorer.net/ 点击直接访问
登录网址后,我们尝试写一段示例代码并观察其AST结构。
基本结构分析及转换
示例vue源码:
以简单的vue demo代码为例:template包含两个p标签作为示例源码,且不包含css相关代码。
<template><p>{{ code }} AST!</p><p>AST 2 TEST!</p>
</template><script>
export default {data () {return {code: "Go"};}
};
</script>
得到的JSON表达的树结构如所示:
简概Json体结构:
{"type": 0,"children": [//指示template标签{"type": 1,"ns": 0,"tag": "template","tagType": 0,"props": [],"isSelfClosing": false,"children": [],"loc": {}},//指示script标签{"type": 1,"ns": 0,"tag": "script","tagType": 0,"props": [],"isSelfClosing": false,"children": [],"loc": {}}],"helpers": [],"components": [],"directives": [],"hoists": [],"imports": [],"cached": 0,"temps": 0,"loc": {}
属性解析:
- type:指代节点的类型,用于标识不同类型的语法单元或操作。例如,可以是"VariableDeclaration"、"FunctionDeclaration"等。
- ns:指代命名空间(Namespace)属性,用于表示XML或HTML文档中某个元素节点所属的命名空间。对于大多数编程语言而言,默认为空即可。
- tag:指代标签(Tag)属性,在HTML或XML文档中使用。它表示一个元素节点所对应的标签名称,例如 “div”, “p”, "span"等。
- tagType:指代标签类型(Tag Type),也只存在于HTML或XML文档中。它描述了特殊的标签行为,并且可能影响解析和渲染过程。常见值包括:“open”,
“close”, “selfClosing”.- props:指代属性集合(Properties),存储与当前节点相关联的所有属性信息。这些信息可以是该节点自身定义的属性、继承自父级别对象、从其他地方引入等等。
- props:指代属性集合(Properties),存储与当前节点相关联的所有属性信息。这些信息可以是该节点自身定义的属性、继承自父级别对象、从其他地方引入等等。
- loc:指代位置信息(Location),描述了源代码中该AST节点对应的位置范围,通常包括行号、列号等信息。这些信息在进行错误定位、调试和格式化时非常有用。
- helpers:指代帮助函数(Helpers),用于存储在转换或编译过程中生成的辅助函数。这些辅助函数通常是为了实现特定功能或处理复杂逻辑而引入的。
- components:指代组件(Components),用于存储当前模块所依赖或使用到的组件信息。这些信息可以包括组件名称、路径、导入声明等。
- directives:指代指令(Directives),用于存储与当前模块相关联的所有自定义指令信息。这些信息可以包括指令名称、参数、修饰符等。
- hoists:指代提升项(Hoists),用于存储需要被提前计算并缓存起来以优化性能的表达式或计算结果。通过将这些表达式移出循环结构,可以减少重复计算次数。
- imports:指代导入项(Imports),用于描述当前模块所引入的外部模块,并记录其对应关系和可访问性等相关信息。
- cached:用于缓存一次求值结果,并在后续多次使用时直接返回缓存值,避免重复计算造成性能损耗。
- temps:临时变量(Temporaries),用于存储在生成的代码中临时使用的变量。这些变量通常是为了辅助实现某个功能或处理过程中所需要的临时数据
AST的特性
我们尝试总结AST结构的数据有什么特性:
- 1. 嵌套层级的结构(递归特性):AST是一种树状结构,由各个节点组成。每个节点代表源代码中的一个语法单元或操作,而父子关系表示了这些语法单元之间的嵌套关系和层次结构。比如template和script作为同级都被嵌套在"type": 0层级的children中。
为什么会是递归?
编程语言中的代码通常具有多级嵌套的结构,例如条件语句、循环语句、函数定义等。为了正确地捕捉这些结构并反映在AST中,递归就变成了一种非常合适的结构。
- 2. 抽象性:AST将源代码转换为更抽象的形式。它会忽略掉一些细节、标点符号和不必要的空格等内容,并着重于捕捉代码的逻辑结构和语义信息。
- 3. 可扩展性:由于AST本身是一个数据结构,在需要进行静态分析、优化或转换时可以方便地对其进行修改和扩展。
这就为我们在Babel中使用@babel/type可以对AST的数据结构进行修改提供了前提。
Babel相关可阅读<Babel>前端语言的巴别塔
总结
随着前端的不断发展,在越来越多的场景下,我们需要对源码进行转换、优化等操作,而AST作为中间的转化产物,提供了通用的方式来代替源码结构,并可以便捷的进行修改,进而生成新的结构输出源码。并且通过遍历和检查AST,我们也可以执行各种如类型检查的静态分析任务。总之,AST已经变成了在百花齐放的前端架构体系下,不可或缺的一门技术。