作者:商见曜

来源:恒生LIGHT云社区

作为现代前端项目必备的一种技术,Babel 是一个编译器,用于将前沿的 JS 语法转换为浏览器支持的语法。接下来就让我们一起深入学习一下吧。

Babel 的介绍

Babel 指的是“巴别塔”,出自圣经典故:

当时的人类只说一种语言,联合起来无所不能,要创建一座通向天堂的塔,上帝害怕了,为了分化他们,于是就让人类说不同的语言,人类因此失去了这种力量,分化了。而这座塔就是巴别塔。

Babel 的用途

  • 转译 esnext,typescript,flow 等到目标环境支持的 js
    这是最常用的功能,用来吧代码中的 esnext 的语法,ts 和 flow 的语法转为基于目标环境支持的语法的实现。并且可以吧目标环境不支持的 api 进行 polyfill
  • 自由自在的编译器
    它暴露了很多的 api,想对源代码进行怎么转译都行
  • 代码的静态分析
    对代码进行 parse 之后,能够进行转换,是因为通过 AST 的结构能够理解代码,在转换之前生成目标代码之外,也可以用于分析代码的信息,进行检查。比如 linter,api 文档自动生成工具通过提取注释生成文档等。

Babel 的转译流程

Babel 是 source to source 的转换,整体转译过程分为三步:

  1. parse: 通过 parser 将源代码转换为抽象语法树 (AST)
  2. transform:遍历 AST,调用各种 transform 插件对 AST 进行增删查改
  3. generate: 把转换后的 AST 打印成目标代码,并生成 sourcemap

为什么是三步而不是一步呢?因为源码太乱了,没有统一的格式,直接 source to source 不现实,于是就加一层 AST,将源代码中混乱的、无意义的部分去掉,这样对 AST 操作就能统一起来了。于是一步就变成了三步。(经典中间件思维了)

简单分析一下这三步
parse

前面已经说过 parse 是将源码转换为统一格式的 AST,这个过程是一个词法分析、语法分析的过程;

比如 let name = ‘shangjianyao’; 这样一段代码,parse 先把它分成一个个不能细分的单词(术语叫 token),得到 let,name,=,‘shangjianyao’,这个过程叫词法分析,按照单词的构成规则拆分源码。

之后要把 token 进行递归的组装,生产 AST,这个过程是语法分析,按照不同的语法规则,把一组单词组合成对象。
也就是说先拆了,再按照新规则组装。

transform

transform 阶段对 parse 生成的 AST 进行处理:对 AST 进行深度优先遍历,遍历的过程中处理不同的 AST 节点会调用注册的相应的 vistor 函数(来源于插件),vistor 函数里可以对 AST 节点进行增删改,返回新的节点。这一步是逻辑处理的核心和多变的关键

generate

这个阶段会把 AST 打印成目标字符串,并且会生成 sourcemap。不同的 AST 对应不同结构的字符串。而 sourcemap 记录了从源码到目标代码的转换关系,通过它可以找到目标代码中每个节点对应的源码位置。

好好聊聊 AST

AST (Abstract Syntax Tree)抽象语法树是整个转译流程的核心,它是一个树状结构,有不同类型的节点,基本对应了 JS 语法里的数据类型和语法,我们分别了解一下:

Literal
Literal 字面量,比如 let name = ‘shangjianyao’ 中,shangjianyao 就是一个字符串字面量 StringLiteral,相应的还有数字字面量 NumericLiteral,布尔字面量 BooleanLiteral,null 字面量 NullLiteral,undefined 字面量 UndefinedLiteral,这些都是 Literal,规则就是这样的 --xxLiteral。

Identifier
也即是标识符的意思,变量名、属性名、参数名等各种声明和引用的名字,都是 Ientifer。(js 里的标识符规则是?Identifer 就是这个识别规则)

试试看,下面这段代码里有多少 identifier?

let name = 'shangjianyao';
function sayHello(name) {console.log('hello ' + name);
}

图里标红色下划线的都是 identifier:

Statement
statement 是语句,它是可以独立执行的单位,比如包含了一些保留字或者关键词比如 break、continue、return,或者流程控制 if、for、while 等,还有声明语句、表达式语句,这些都是 statement。一般来说,我们写的每一条可以独立执行的代码都是语句。
下面是一些常见的语句,每一行是一个 statement:

break;
continue;
return;
debugger;
throw Error();
{}
try {} catch (e) {} finally {}
for (;;) {}
while (true) {}
do {} while (true);
switch (x) { case 1: break; default: ;}
console.log();
with (x) {}

stament 的名字组合也是和 literal 的规则相似,都是 xxStatement,比如 break 对应 BreakStament。

Declaration

声明语句,是一种特殊的语句,它执行的逻辑是在作用域里声明一个变量、函数、class,import、export 等。
比如:

const name = 'shangjianyao';
function sayHello(name) {}
class Person {}import {name} from './name';export {name};
export default name;
export * from './name';

对应 DeclarationStatement,比如:

Expression

expression 是表达式,特点是执行完以后有返回值,这是和语句(statement)的区别。
常见表达式有:

[1,2,3];
name = 'shangjianyao';
1+1;
-1;
function (){}
() => {}
class {}
name;
this;
super;
a::b;

对应的 expression 是 xxExpression,比如:

对于上面的表达式里面,怎么混入了 identifier?因为 identifier 会返回值,所以他也是 expression。有些表达式不能单独执行比如匿名函数表达式和匿名 class 表达式,需要和其他部分组成一个 statement。表达式在被 parse 时会包裹一层 ExpressionStatement,标识这个表达式是被当成语句执行的。

Class

作为重要语法糖,class 也有专门的 AST 节点标识。

整个 class 的内容是 ClassBody,属性是 ClassProperty,方法是 MethodDefinition,通过 MethodDefinition 字段来区分构造函数和普通方法。
比如下面这段代码:

class Person {constructor(name) {this.name = name;}sayHello() {console.log('hello ' + this.name);}
}

对应的 AST

Modules
es module 是语法级别的模块规范,所以也有专门的 AST 节点。

// name import
import {name} from './name';
// default import
import name from './name';
// namespace import
import * as name from './name';

对应的 AST

有不同的语法就有不同的 importDeclaration 节点,通过 specifiers 字段来区分 import 的类型,分别对应为 importSpecifier、importDefaultSpecifier、importNamespaceSpecifier。

// name export
export { name };
// default export
export default name;
// all export
export * from './name';

对应的 AST

分别对应 ExportNameDeclaration、ExportDefaultDeclaration、ExportAllDeclaration。只有 ExportNameDeclaration 才有 specifiers 字段。

Program & Directive

program 是代表整个程序的节点,他有一个 body 字段,是一个数组,里面存放了所有的 statement,执行语句的集合。directives 属性存放 Directive 节点,比如 use strict。
Program 是包裹具体执行语句的节点,而 Directive 则是代码中的指令。

File & Comment

Babel 的 AST 最外层节点是 File,它有 program、comments、tokens 等属性,分别存放 Program 程序体、注释、token 等,是最外层的节点。注释分为块注释(/**/) 和行注释(//)。

公共属性
每种节点都有自己的属性用来标识身份,自然地,也需要一些公共属性去构建和维持 AST,比如:

  • type: AST 节点的类型
  • start、end、loc:start 和 end 代表该节点对应的源码字符串的起始和结束下标,不区分行列。loc 是一个对象,有 line 和 column 属性分别记录开始和结束行列号。
  • leadingComments,innerComments,trailingComments:leadingComments、innerComments、trailingComments 分别存放前面注释,中间注释,后面注释。
  • extra: 存放一些额外的信息,比如 StringLiteral 修改 value 只是值的修改,而修改 extra.raw 则可以连同单双引号一起修改。

AST 可视化查看工具

我们不需要记住上面的这些太多,需要的时候到可视化的 AST 里查看即可。

或者查阅 Babel parser 仓库的 AST。
或者查看 @Babel/types 的 typescript 类型定义

小结

本文简单介绍了一下 Babel 的用途:将超前语法转译为兼容语法。
介绍了 Babel 的转译过程:source code -> AST -> transformed AST -> code + sourcemap
介绍了 AST 是如何抽象源码的:利用各种节点来描述源码。比如标识符 Identifier,表达式 xxExpression,语句 xxStatement,声明语句 xxDeclaration, 字面量 xxLiteral,class,modules,file,Program,Directicve,Comment 等等。了解有哪些节点就知道怎么用 AST 标识源码了,当然也不需要记,用(astexpoler.net)进行可视化的查看更加理想。
下一节,我们将会学习 Babel 的 api,以及进行一次简单的实战,下期再见。✈️


想向技术大佬们多多取经?开发中遇到的问题何处探讨?如何获取金融科技海量资源?

恒生LIGHT云社区,由恒生电子搭建的金融科技专业社区平台,分享实用技术干货、资源数据、金融科技行业趋势,拥抱所有金融开发者。

Babel 学习日记(0)相关推荐

  1. Axiom3D学习日记 0.Axiom基础知识

    Axiom 3D Engine An open-source, cross-platform, managed 3D rendering engine for DirectX, XNA and Ope ...

  2. wisp5.0 学习日记2

    学习日记 昨天的报错 尝试1,在CCS中设置USB FET 尝试2 csdn解决方案1 尝试3 查看仿真器的驱动是否安装成功 昨天的报错 MSP430: Error initializing emul ...

  3. java的圆周率_java学习日记,圆周率的打印

    前段时间看到听说学习java每天写技术贴会对自己提升很大,我现在学习java也就2个周,算不上技术贴,就写写学习日记吧. 昨天师傅给我出了一道题,说是试试用java打印圆周率. 刚开始我的思路是,如果 ...

  4. GPU(CUDA)学习日记(十三)------ CUDA内存简介

    GPU(CUDA)学习日记(十三)------ CUDA内存简介 标签: cuda存储线程结构 2012-12-07 16:53 2902人阅读 评论(0)收藏 举报 分类: GPU(16) CUDA ...

  5. GPU(CUDA)学习日记(九)------ CUDA存储器模型

    GPU(CUDA)学习日记(九)------ CUDA存储器模型 标签: cuda存储bindingcache编程api 2012-09-27 10:53 1677人阅读 评论(1) 收藏 举报 分类 ...

  6. 语言余一个负数_C语言学习日记(8)——整数溢出

    小时候喜欢玩电子计算器,觉得很神奇,想要算什么数,立刻就能算出来.当时看着计算器的液晶屏幕就想,如果算一个很大的数,超过了屏幕上面数字的位数,会怎么样呢?试了以后结果是INF,那就是无穷大了.虽然当时 ...

  7. SpringMVC学习日记 1.Spring框架

    SpringMVC学习日记 1.Spring框架 Spring简介 Spring框架是一个开源框架,由Rod Johnson组织和开发,生产目的在于简化企业级应用的开发. 主要特性 非侵入(no-in ...

  8. 十五的学习日记20160926-你不知道的JS笔记/

    十五的学习日记20160926 JavaScript 一个用于检测正负值的函数,可以用它辨别-0值. 我觉得挺好用,以后可以写到自己的工具库里. //函数:检查传入参数是否为正数.Number=> ...

  9. 十五的学习日记20160925

    十五的学习日记20160925 CSS 学过盒子布局的人都知道,元素之间的上下margin会合并,保留数值较大的margin作为渲染依据. 但是今天在群里讨论发现: img元素和p元素的上下margi ...

最新文章

  1. ajax请求php报500错误,ajax只能发起GET请求,而不能发POST请求,否则报500错误
  2. 第三天2017/03/30(上午:二级指针的(输入)内存模型:(共三种模型))
  3. 第六次实训作业异常处理
  4. JavaScript 概述
  5. 排序算法部分知识点小结
  6. python中pip作用_python中的pip有什么作用
  7. js判断移动端还是pc端
  8. 这是你了解的P2P 么?
  9. matlab 菱形符号,matlab 图形符号
  10. 基于Matlab的机器人学建模学习资料大整理
  11. 【新手入门】deepfacelab的电脑配置要求
  12. 学python之前需要学什么,学好python需要什么基础
  13. Day13-寻觅踪迹
  14. basler恢复出厂设置_实现图像实时采集(使用BaslerSDK)-C
  15. 港科夜闻|香港科技大学史维校长及汪扬副校长出席“一流大学建设系列研讨会--2021”暨中国大学校长联谊会线上会议...
  16. 有限差分法和有限体积法的区别
  17. 2D弹簧质点系统的隐式求解
  18. 2021年美容师(中级)复审考试及美容师(中级)模拟考试题
  19. 1098: 零起点学算法05——除法算术题
  20. On hand QuantitiesTransaction Qantiites

热门文章

  1. 设计模式之里氏替换原则
  2. 关于图片锯齿产生的原因和如何消除
  3. 微信小程序苹果手机statusBarHeight状态栏高度为0
  4. 【kafka】二、kafka安装
  5. RFID技术是怎么构成的,主要分为哪几部分?
  6. Java面向对象编程之继承练习题(三)
  7. uniapp 上传图片 + 预览图片 + 删除图片
  8. 溢米辅导完成C轮1500万美元融资,将用于教研、产品以及技术三大领域
  9. 10号前发工资和15号后发工资有啥区别
  10. 《数据出境安全评估办法》将正式施行,聊聊数据出境安全合规那些事