文章: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. Node.js使用UDP通讯

    Node.js 的 dgram 模块可以方便的创建udp服务,,以下是使用 dgram模块创建的udp服务和客户端的一个简单例子. 一.创建 UDP Server var dgram = requir ...

  2. a标签属性 rel=noopener noreferrer

    当你浏览一个页面点击一个a标签链接跳转到另一个页面时, <a href="http://www.baidu.com" target="_blank"> ...

  3. NGINX.conf配置文件支持pathinfo

    # power by www.php.cn #user nobody; worker_processes 1; #error_log logs/error.log; #error_log logs/e ...

  4. MySQL基础(一):基本操作

    一.下载安装及连接 MySQL是一个关系型数据库管理系统,由瑞典MySQL AB 公司开发,目前属于 Oracle 旗下公司.MySQL 最流行的关系型数据库管理系统,在 WEB 应用方面MySQL是 ...

  5. 页面用一个遮罩层显示加载,加载完后隐藏该div

    <div id="background" class="background" style="display: none; "> ...

  6. C# 多线程之Thread类

    使用System.Threading.Thread类可以创建和控制线程. 常用的构造函数有:   // 摘要: // 初始化 System.Threading.Thread 类的新实例,指定允许对象在 ...

  7. kubernetes配置secret拉取私仓镜像

    2017.05.10 19:48* 字数 390 阅读 5216评论 0喜欢 8 对于公司内部的项目, 我们不可能使用公有开放的镜像仓库, 一般情况可能会花钱买docker私仓服务, 或者说自己在服务 ...

  8. HttpDebug下载

    话不多说,早就有了这个,有一天公司地址有限制,网盘访问不了,看见很多博客园上的下载都需要积分,忍不了就发出来共享吧! HttpDebug下载: https://files.cnblogs.com/fi ...

  9. bzoj千题计划253:bzoj2154: Crash的数字表格

    http://www.lydsy.com/JudgeOnline/problem.php?id=2154 #include<cstdio> #include<algorithm> ...

  10. jdk 动态代理实现对目标对象的增强

    因为学习 spring框架的 AOP 所以,这里复习下动态代理,但是 spring 的底层动态代理实现是 cglib,jdk 这种呢可以引导新手入门 spring AOP 横向抽取机制 最典型的应用当 ...