前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >AST 基础学习以及躲坑技巧

AST 基础学习以及躲坑技巧

作者头像
libo1106
发布2018-12-25 12:04:08
1K0
发布2018-12-25 12:04:08
举报
文章被收录于专栏:Web 开发Web 开发Web 开发

AST 的全称是抽象语法树,名字也很抽象,给个容易理解的例子。

JavaScript

const blog = 'mxgw.info';

1

const blog = 'mxgw.info';

上面这个例子,是一个简单的常量定义。

当浏览器不支持 const 这种语法的时候,我们需要把他换成支持的 var,这个时候,AST 就上场了。

这里面,每一个包含 type 的层次结构,都叫一个节点(Node)。

这里我们关注的 const 就在一个 VariableDeclaration 的节点上面,开始位置为 0。

一个个 Node 节点,组成了一份描述我们代码的树状结构,也就是 AST。

Babel 的处理步骤

解析(parse) > 转换(transform) > 生成(generate)

解析

解析是指接收代码,并输出 AST。

这其中又包含词法分析(Lexical Analysis)和语法分析(Syntactic Analysis)。

词法分析和语法分析在这不展开,有很多库帮我们直接拿到代码的 AST,比如 acorn 和 babylon。

转换

转换就是对 AST 进行遍历,并在过程中对所需的节点(Node)进行修改操作。像上面案例中的 constvar 就是这个阶段进行的。

生成

把修改后的 AST,变成字符串形式的代码,这里还可以顺便做一下 source maps。

如何进行最复杂的转换?

1、我们要对 AST 进行深度优先的遍历,遍历每一个节点。

2、在 AST 领域,有一个叫访问者模式(visitor)的概念,用 visitor 来访问每个节点和里面的属性。

JavaScript

const MyVisitor = { VariableDeclaration: { enter() { console.log("进入函数声明节点"); }, exit() { console.log("离开函数声明节点"); } } };

12345678910

const MyVisitor = {  VariableDeclaration: {    enter() {      console.log("进入函数声明节点");    },    exit() {      console.log("离开函数声明节点");    }  }};

在遍历的过程,我们有进入和离开两次访问节点的机会,就像入栈出栈一样。

3、当 visitor 来访问每个节点的时候,仅有的节点信息和属性信息,不够我们做出任何决策。我们需要知道更多的信息,例如当前节点和其他节点的关系,而这种关系,就用路径(Paths)来描述。在 Babel 的 visitor 里面,拿到的参数就是路径。

到这里为止,我们就可以对我们想修改的代码,生成代码的 AST,然后遍历,使用 visitor 进行修改。

转换过程中的坑

1、状态(State)

我们想转换某个函数里面的某个变量,结果直接在 Identifier 里面转换,导致把全部的变量都给转换掉了。

正确的做法是在 FunctionDeclaration 的访问者里面通过递归来做 Identifier 转换。

2、作用域(Scopes)

除了上面通过递归方式,来减少错误的变量转换外,我们的变量还有可能是在外层函数做的定义,visitor 拿到的外层函数中的一个引用,此时贸然修改,会导致意外发生。

结语

到这里为止,AST 的基本概念都科普完了,对 AST 可以做些什么也心里有数了。

随着技术的进步和环境的复杂化,未来的 polyfill 集合一定会越来越庞大。不论是静态补丁(@babel/polyfill 或 @babel/plugin-transform-runtime)还是动态补丁(polyfill.io)都将产生大量的冗余代码。

而未来的 polyfill 应该是

1、在编译阶段就获得代码中用到的 ES5(这个下限应该要可以根据时间或者 .browserslistrc 的信息进行调整)以上的 API 集合。

2、在浏览器运行的时候,对 API 做特征检测,获得实际浏览器所需的 API 子集合。

3、向类似 polyfill.io 这种动态服务请求这个子集合的 polyfill。

其实 polyfill.io 在 2014 年就有这方面的讨论了,但我觉得脱离了第一步而直接做第二步的实施特征检测,依旧会得到一个超大集合的代码,并且是随着技术进步而越来越大。

参考

大部分内容都是从 babel-handbook 中学习的。

https://github.com/jamiebuilds/babel-handbook/blob/master/translations/zh-Hans/plugin-handbook.md

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2018-12-062,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Babel 的处理步骤
    • 解析
      • 转换
        • 生成
        • 如何进行最复杂的转换?
          • 转换过程中的坑
          • 结语
          • 参考
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档