文章:https://juejin.im/post/5a9315e46fb9a0633a711f25

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

  1. 你了解过Babel吗?

了解过抽象语法树,又称AST,有学习过,也写过一个基于AST的乞丐版模板引擎,先是词法解析token,然后生产抽象语法树,然后更改抽象语法树,当然这是插件做的事情,最后根据新的AST生成代码。

  1. 写过Babel插件吗

没有,只是看过相关文档

  1. 如果让你写一个插件,你能写的出来吗?

应该可以吧...

遂卒....

开玩笑的,既然提到了,又没回答上来什么,哎哟我这暴脾气,一想到今晚就睡不着,连夜把它撸了。

那么我们来从零写个插件吧。

写一个预计算简单表达式的插件

预览

Before:

const result = 1 + 2 + 3 + 4 + 5;

After:

const result = 15;

以上的例子可能大家不会经常遇到,因为傻x才会这么写,但是有可能你会这么写

setTimeout(function(){
// do something
}, 1000 * 2) // 插件要做的事,就是把 1000 * 2 替换成 2000

前提条件

开工

再写代码之前,你需要明白Babel它的原理,简单点说: Babel解析成AST,然后插件更改AST,最后由Babel输出代码

那么Babel的插件模块需要你暴露一个function,function内返回visitor

module.export = function(babel){
return {
visitor:{
}
}
}

visitor是对各类型的AST节点做处理的地方,那么我们怎么知道Babel生成了的AST有哪些节点呢?

很简单,你可以把Babel转换的结果打印出来,或者这里有传送门: AST explorer

这里我们看到 const result = 1 + 2中的1 + 1是一个BinaryExpression节点,那么在visitor中,我们就处理这个节点

var babel = require('babel-core');
var t = require('babel-types'); const visitor = {
BinaryExpression(path) {
const node = path.node;
let result;
// 判断表达式两边,是否都是数字
if (t.isNumericLiteral(node.left) && t.isNumericLiteral(node.right)) {
// 根据不同的操作符作运算
switch (node.operator) {
case "+":
result = node.left.value + node.right.value;
break
case "-":
result = node.left.value - node.right.value;
break;
case "*":
result = node.left.value * node.right.value;
break;
case "/":
result = node.left.value / node.right.value;
break;
case "**":
let i = right;
while (--i) {
result = result || node.left.value;
result = result - node.left.value;
}
break;
default:
}
} // 如果上面的运算有结果的话
if (result !== undefined) {
// 把表达式节点替换成number字面量
path.replaceWith(t.numericLiteral(result));
}
}
}; module.exports = function (babel) {
return {
visitor
};
}

插件写好了,我们运行下插件试试

const babel = require("babel-core");

const result = babel.transform("const result = 1 + 2;",{
plugins:[
require("./index")
]
}); console.log(result.code); // const result = 3;

与预期一致,那么转换 const result = 1 + 2 + 3 + 4 + 5;呢?

结果是: const result = 3 + 3 + 4 + 5;

这就奇怪了,为什么只计算了1 + 2之后,就没有继续往下运算了吗?

我们看一下这个表达式的AST树

你会发现Babel解析成表达式里面再嵌套表达式。

表达式( 表达式( 表达式( 表达式(1 + 2) + 3) + 4) + 5)

而我们的判断条件并不符合所有的,只符合1 + 2

    // 判断表达式两边,是否都是数字
if (t.isNumericLiteral(node.left) && t.isNumericLiteral(node.right)) {}

那么我们得改一改

第一次计算1 + 2之后,我们会得到这样的表达式

表达式( 表达式( 表达式(3+ 3) + 4) + 5)

其中 3 + 3又符合了我们的条件, 我们通过向上递归的方式遍历父级节点

    // 如果上面的运算有结果的话
if (result !== undefined) {
// 把表达式节点替换成number字面量
path.replaceWith(t.numericLiteral(result)); let parentPath = path.parentPath; // 向上遍历父级节点
parentPath && visitor.BinaryExpression.call(this, parentPath);
}

到这里,我们就得出了结果 const result = 15;

那么其他运算呢:

const result = 100 + 10 - 50 >>> const result = 60;

const result = (100 / 2) + 50 >>> const result = 100;

const result = (((100 / 2) + 50 * 2) / 50) ** 2 >>> const result = 9;

完结

到这里,已经向你大概的讲解了,如果编写一个Babel插件,再也不怕面试官问我答不出什么了哈...

你以为这就完了吗?

并没有

如果转换这样呢: const result = 0.1 + 0.2;

预期肯定是0.3, 但是实际上,Javascript有浮点计算误差,得出的结果是0.30000000000000004

那是不是这个插件就没卵用?

这就需要你去矫正浮点运算误差了,可以使用Big.js;

比如: result = node.left.value + node.right.value; 改成 result = +new Big(node.left.value).plus(node.right.value);

你以为完了吗? 这个插件还可以做很多

比如: Math.PI * 2 >>> 6.283185307179586

比如: Math.pow(2,2) >>> 4

...

...

最后上项目地址: github.com/axetroy/bab…

作者:Axetroy
链接:https://juejin.im/post/5a9315e46fb9a0633a711f25
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

Babel(抽象语法树,又称AST)的更多相关文章

  1. 五分钟了解抽象语法树(AST)babel是如何转换的?

    抽象语法树 什么是抽象语法树? It is a hierarchical program representation that presents source code structure acco ...

  2. JavaScript的工作原理:解析、抽象语法树(AST)+ 提升编译速度5个技巧

    这是专门探索 JavaScript 及其所构建的组件的系列文章的第 14 篇. 如果你错过了前面的章节,可以在这里找到它们: JavaScript 是如何工作的:引擎,运行时和调用堆栈的概述! Jav ...

  3. 新抽象语法树(AST)给 PHP7 带来的变化

    本文大部分内容参照 AST 的 RFC 文档而成:https://wiki.php.net/rfc/abstract_syntax_tree,为了易于理解从源文档中节选部分进行介绍. 我的官方群点击此 ...

  4. 抽象语法树(AST)

    AST描述 在计算机科学中,抽象语法树(AST)或语法树是用编程语言编写的源代码的抽象语法结构的树表示.树的每个节点表示在源代码中出现的构造.语法是“抽象的”,因为它不代表真实语法中出现的每个细节,而 ...

  5. 理解Babel是如何编译JS代码的及理解抽象语法树(AST)

    Babel是如何编译JS代码的及理解抽象语法树(AST) 1. Babel的作用是?   很多浏览器目前还不支持ES6的代码,但是我们可以通过Babel将ES6的代码转译成ES5代码,让所有的浏览器都 ...

  6. AST抽象语法树

    抽象语法树简介 (一)简介 抽象语法树(abstract syntax code,AST)是源代码的抽象语法结构的树状表示,树上的每个节点都表示源代码中的一种结构,这所以说是抽象的,是因为抽象语法树并 ...

  7. javascript编写一个简单的编译器(理解抽象语法树AST)

    javascript编写一个简单的编译器(理解抽象语法树AST) 编译器 是一种接收一段代码,然后把它转成一些其他一种机制.我们现在来做一个在一张纸上画出一条线,那么我们画出一条线需要定义的条件如下: ...

  8. 从零写一个编译器(九):语义分析之构造抽象语法树(AST)

    项目的完整代码在 C2j-Compiler 前言 在上一篇完成了符号表的构建,下一步就是输出抽象语法树(Abstract Syntax Tree,AST) 抽象语法树(abstract syntax ...

  9. AST抽象语法树 Javascript版

    在javascript世界中,你可以认为抽象语法树(AST)是最底层. 再往下,就是关于转换和编译的"黑魔法"领域了. 现在,我们拆解一个简单的add函数 function add ...

随机推荐

  1. Docker(一)-Docker介绍

    什么就Docker? Docker是一个开源项目, 诞生于2013年初,最初是dotCloud公司内部的一个业余项目.它基于Google公司推出的Go语言实现.项目后来加入了Linux基金会,遵从了A ...

  2. 动态sql中的条件判断取值来源于map 或者 model

  3. Java重写toString和泛型的使用

    一.在一个类中重写toString方法 public class Person { private String name; private int age; public Person() { } ...

  4. java Random.nextInt()方法

    转: java Random.nextInt()方法 lic int nextInt(int n) 该方法的作用是生成一个随机的int值,该值介于[0,n)的区间,也就是0到n之间的随机int值,包含 ...

  5. Kafka 0.8 宕机问题排查步骤

    CPU 利用率高的排查方法 看看该机器的连接数是不是比其他机器多,监听的端口数:netstat -anlp | wc -l Kafka-0.8的停止和启动 启动: cd /usr/local/kafk ...

  6. POJ - 3026 Borg Maze(最小生成树)

    https://vjudge.net/problem/POJ-3026 题意 在一个y行 x列的迷宫中,有可行走的通路空格’ ‘,不可行走的墙’#’,还有两种英文字母A和S,现在从S出发,要求用最短的 ...

  7. POJ - 2240 Arbitrage(Bellman-Ford)

    https://vjudge.net/problem/POJ-2240 题意 已知n种货币,以及m种货币汇率及方式,问能否通过货币转换,使得财富增加. 分析 Bellman-Ford判断正环,注意初始 ...

  8. HDU 2509 基础Anti-SG NIM

    如果我们规定当局面中所有的单一游戏的SG值为0时,游戏结束,则先手必胜当且仅当:(1)游戏的SG!=0 && 存在单一游戏的SG>1:(2)游戏的SG==0  && ...

  9. linux离线部署redis及redis.conf详解

    一.离线部署redis 由于博主部署的虚拟机没有网络也没有gcc编译器,所以就寻找具备gcc编译器的编译环境把redis编译安装好,Copy Redis安装目录文件夹到目标虚拟机的目录下.copy时r ...

  10. 【LibreOJ】#6396. 「THUPC2018」弗雷兹的玩具商店 / Toyshop 线段树+完全背包

    [题目]#6396. 「THUPC2018」弗雷兹的玩具商店 / Toyshop [题意]给定一个长度为n的物品序列,每个物品有价值.不超过m的重量.要求支持以下三种操作:1.物品价值区间加减,2.物 ...