webpack 和 Lint 等很多的工具和库的核心都是通过 Abstract Syntax Tree 抽象语法树这个概念来实现对代码的校验、分析等操作的。

用途

代码语法的检查、代码风格的检查、代码的格式化、代码的高亮、代码的错误提示、代码的自动补全

  • 如 LSLint,JSHint 对代码错误或风格的检查,发现一些潜在的错误。
  • IDE的错误提示、格式化、高亮、自动补全等

代码混淆压缩

  • UglifyJS2 等

优化变更代码,改变代码结构使达到想要的结构

  • 代码打包工具 webpack,rollup 等等
  • CommonJS,AMD,CMD,UMD等代码规范之间的转化
  • CoffeeScript、TypeScript、JSX 等转化为原生 JavaScript

    抽象语法树定义

    这些工具的原理都是通过 JavaScript Parser 把代码转化为一颗抽象语法树(AST),这棵树定义了代码结构,通过操纵这棵树,我们可以精准的定位到声明语句、赋值语句、运算语句等等,实现对代码的分析、优化、变更的操作。

  • keyword 关键字

  • identifier 标识符
  • equal 等于号
  • string 字符串
  • semicoion 分号

抽象语法树(AST) - 图1

JavaScript Parser

  • JavaScript Parser 是把 JavaScript 源码转化为抽象语法树的解析器。
  • 浏览器会把 JavaScript 源码通过解析器转化为抽象语法树,再进一步转化为字节码或者直接生成机器码。
  • 一般来说每个 JavaScript 引擎都会有自己的抽象语法树格式,Chrome 的 v8 引擎,firefox 的 SpiderMonKey 引擎等等,MDN提供了详细 SpiderMonkey AST format 的详细说明,算是业界标准。

    常用的 JavaScript Parser

  • esprima

  • traceur
  • acorn
  • shift

    AST explorer

    可以在线将 js 代码转换成抽象语法树。
    https://astexplorer.net/

    生成-遍历-重新生成

    这里我们用到 3 个包分别代表着三个过程

  • esprima 它可以把源代码转换成抽象语法树

  • estraverse 它可以遍历语法树,修改树上的语法节点
  • escodegen 它可以重新生成源代码

如下例子,将我们定义的 function的名字生成了语法树,并且打印了节点的类型,最后将 ast 改成了 newFunction

  1. // 他可以把源代码转换成抽象语法树
  2. let esprima = require('esprima');
  3. // 他可以遍历语法树,修改树上的语法节点
  4. let estraverse = require('estraverse');
  5. // 它可以重新生成源代码
  6. let escodegen = require('escodegen');
  7. // 定义一段源代码
  8. let sourceCode = 'function ast(){}';
  9. // ast 就是生成的语法树
  10. let ast = esprima.parse(sourceCode);
  11. let indent = 0;
  12. const padding = () => ' '.repeat(indent);
  13. // 遍历语法树,深度优先遍历
  14. estraverse.traverse(ast, {
  15. //进入
  16. enter(node, parent) {
  17. console.log(padding() + node.type);
  18. if(node.type === 'FunctionDeclaration'){
  19. node.id.name = 'newFunction';
  20. }
  21. indent++;
  22. },
  23. //出去
  24. leave(node, parent) {
  25. indent--;
  26. console.log(padding() + node.type);
  27. }
  28. })
  29. //重新生成源代码
  30. let newSourceCode = escodegen.generate(ast);
  31. console.log(newSourceCode);

打印出的结果为
image.png