内容简介:Babel 是一个通用的多功能的 JavaScript 编译器。此外它还拥有众多模块可用于不同形式的静态分析。静态分析是在不需要执行代码的前提下对代码进行分析的处理过程 (执行代码的同时进行代码分析即是动态分析)。 静态分析的目的是多种多样的, 它可用于语法检查,编译,代码高亮,代码转换,优化,压缩等等场景。
Babel 是 JavaScript 编译器 compiler ,更确切地说是源码到源码的编译器,通常也叫做 转换编译器(transpiler)
。 意思是说你为 Babel 提供一些 JavaScript 代码,Babel 更改这些代码,然后返回给你新生成的代码。
Babel 是一个通用的多功能的 JavaScript 编译器。此外它还拥有众多模块可用于不同形式的静态分析。
静态分析是在不需要执行代码的前提下对代码进行分析的处理过程 (执行代码的同时进行代码分析即是动态分析)。 静态分析的目的是多种多样的, 它可用于语法检查,编译,代码高亮,代码转换,优化,压缩等等场景。
Babylon 是 Babel 的解析器 parser 。最初是 从 Acorn 项目 fork 出来的。Acorn 非常快,易于使用,并且针对非标准特性(以及那些未来的标准特性) 设计了一个基于插件的架构。
Babylon 已经移入 Babel mono-repo 更名为 babel-parser
首先,让我们安装它。
$ npm install --save babylon 复制代码
先从解析一个代码字符串开始:
import * as babylon from "babylon"; const code = `function square(n) { return n * n; }`; babylon.parse(code); // Node { // type: "File", // start: 0, // end: 38, // loc: SourceLocation {...}, // program: Node {...}, // comments: [], // tokens: [...] // } 复制代码
我们还能像下面这样传递选项给 parse()
方法:
babylon.parse(code, { sourceType: "module", // default: "script" plugins: ["jsx"] // default: [] }); 复制代码
sourceType
可以是 "module"
或者 "script"
,它表示 Babylon 应该用哪种模式来解析。 "module"
将会在严格模式下解析并且允许模块定义, "script"
则不会。
注意: sourceType
的默认值是 "script"
并且在发现 import
或 export
时产生错误。 使用 scourceType: "module"
来避免这些错误。
由于 Babylon 使用了基于插件的架构,因此有一个 plugins
选项可以开关内置的插件。 注意 Babylon 尚未对外部插件开放此 API 接口,不排除未来会开放此API。
解析(Parse)
解析(Parse )步骤接收代码并输出 抽象语法树(AST)
。 这个步骤分为两个阶段: **词法分析(Lexical Analysis) ** 和 语法分析(Syntactic Analysis) 。
词法分析
词法分析阶段把字符串形式的代码转换为 令牌(tokens) 流。
你可以把令牌看作是一个扁平的语法片段数组:
n * n; 复制代码
[ { type: { ... }, value: "n", start: 0, end: 1, loc: { ... } }, { type: { ... }, value: "*", start: 2, end: 3, loc: { ... } }, { type: { ... }, value: "n", start: 4, end: 5, loc: { ... } }, ... ] 复制代码
每一个 type
有一组属性来描述该令牌:
{ type: { label: 'name', keyword: undefined, beforeExpr: false, startsExpr: true, rightAssociative: false, isLoop: false, isAssign: false, prefix: false, postfix: false, binop: null, updateContext: null }, ... } 复制代码
和 AST 节点一样它们也有 start
, end
, loc
属性。
语法分析
语法分析阶段会把一个令牌流转换成 抽象语法树(AST)
的形式。 这个阶段会使用令牌中的信息把它们转换成一个 AST 的表述结构,这样更易于后续的操作。
这个处理过程中的每一步都涉及到创建或是操作抽象语法树,亦称 AST。
Babel 使用一个基于 ESTree 并修改过的 AST,它的内核说明文档可以在[这里](https://github. com/babel/babel/blob/master/doc/ast/spec. md)找到。
function square(n) { return n * n; } 复制代码
AST Explorer 可以让你对 AST 节点有一个更好的感性认识。这里是上述代码的一个示例链接。
这个程序可以被表示成如下所示的 JavaScript Object(对象):
{ type: "FunctionDeclaration", id: { type: "Identifier", name: "square" }, params: [{ type: "Identifier", name: "n" }], body: { type: "BlockStatement", body: [{ type: "ReturnStatement", argument: { type: "BinaryExpression", operator: "*", left: { type: "Identifier", name: "n" }, right: { type: "Identifier", name: "n" } } }] } } 复制代码
你会留意到 AST 的每一层都拥有相同的结构:
{ type: "FunctionDeclaration", id: {...}, params: [...], body: {...} } 复制代码
{ type: "Identifier", name: ... } 复制代码
{ type: "BinaryExpression", operator: ..., left: {...}, right: {...} } 复制代码
注意:出于简化的目的移除了某些属性
这样的每一层结构也被叫做 节点(Node) 。 一个 AST 可以由单一的节点或是成百上千个节点构成。 它们组合在一起可以描述用于静态分析的程序语法。
每一个节点都有如下所示的接口(Interface):
interface Node { type: string; } 复制代码
字符串形式的 type
字段表示节点的类型(如: "FunctionDeclaration"
, "Identifier"
,或 "BinaryExpression"
)。 每一种类型的节点定义了一些附加属性用来进一步描述该节点类型。
Babel 还为每个节点额外生成了一些属性,用于描述该节点在原始代码中的位置。
{ type: ..., start: 0, end: 38, loc: { start: { line: 1, column: 0 }, end: { line: 3, column: 1 } }, ... } 复制代码
每一个节点都会有 start
, end
, loc
这几个属性。
变量声明
代码
let a = 'hello' 复制代码
AST
VariableDeclaration
变量声明, kind
属性表示是什么类型的声明,因为 ES6 引入了 const/let
。 declarations
表示声明的多个描述,因为我们可以这样: let a = 1, b = 2;
。
interface VariableDeclaration <: Declaration { type: "VariableDeclaration"; declarations: [ VariableDeclarator ]; kind: "var"; } 复制代码
VariableDeclarator
变量声明的描述, id
表示变量名称节点, init
表示初始值的表达式,可以为 null
。
interface VariableDeclarator <: Node { type: "VariableDeclarator"; id: Pattern; init: Expression | null; } 复制代码
Identifier
标识符,我觉得应该是这么叫的,就是我们写 JS 时自定义的名称,如变量名,函数名,属性名,都归为标识符。相应的接口是这样的:
interface Identifier <: Expression, Pattern { type: "Identifier"; name: string; } 复制代码
一个标识符可能是一个表达式,或者是解构的模式(ES6 中的解构语法)。我们等会会看到 Expression
和 Pattern
相关的内容的。
Literal
字面量,这里不是指 []
或者 {}
这些,而是本身语义就代表了一个值的字面量,如 1
, “hello”
, true
这些,还有正则表达式(有一个扩展的 Node
来表示正则表达式),如 /\d?/
。我们看一下文档的定义:
interface Literal <: Expression { type: "Literal"; value: string | boolean | null | number | RegExp; } 复制代码
value
这里即对应了字面量的值,我们可以看出字面量值的类型,字符串,布尔,数值, null
和正则。
二元运算表达式
代码
let a = 3+4 复制代码
AST
BinaryExpression
二元运算表达式节点, left
和 right
表示运算符左右的两个表达式, operator
表示一个二元运算符。
interface BinaryExpression <: Expression { type: "BinaryExpression"; operator: BinaryOperator; left: Expression; right: Expression; } 复制代码
BinaryOperator
二元运算符,所有值如下:
enum BinaryOperator { "==" | "!=" | "===" | "!==" | "<" | "<=" | ">" | ">=" | "<<" | ">>" | ">>>" | "+" | "-" | "*" | "/" | "%" | "|" | "^" | "&" | "in" | "instanceof" } 复制代码
if 语句
代码
if(a === 0){ } 复制代码
AST
IfStatement
if
语句节点,很常见,会带有三个属性, test
属性表示 if (...)
括号中的表达式。
consequent
属性是表示条件为 true
时的执行语句,通常会是一个块语句。
alternate
属性则是用来表示 else
后跟随的语句节点,通常也会是块语句,但也可以又是一个 if
语句节点,即类似这样的结构: if (a) { //... } else if (b) { // ... }
。 alternate
当然也可以为 null
。
interface IfStatement <: Statement { type: "IfStatement"; test: Expression; consequent: Statement; alternate: Statement | null; } 复制代码
常见的 AST node types
常见的 AST node types 在 Babylon 中 定义如下:
Node objects
符合规范的解析出来的 AST 节点用 Node
对象来标识, Node
对象应该符合这样的接口:
interface Node { type: string; loc: SourceLocation | null; } 复制代码
type
字段表示不同的节点类型,下边会再讲一下各个类型的情况,分别对应了 JavaScript 中的什么语法。 loc
字段表示源码的位置信息,如果没有相关信息的话为 null
,否则是一个对象,包含了开始和结束的位置。接口如下:
interface SourceLocation { source: string | null; start: Position; end: Position; } 复制代码
这里的 Position
对象包含了行和列的信息,行从 1 开始,列从 0 开始:
interface Position { line: number; // >= 1 column: number; // >= 0 } 复制代码
Identifier
标识符,就是我们写 JS 时自定义的名称,如变量名,函数名,属性名,都归为标识符。相应的接口是这样的:
interface Identifier <: Expression, Pattern { type: "Identifier"; name: string; } 复制代码
一个标识符可能是一个表达式,或者是解构的模式(ES6 中的解构语法)。我们等会会看到 Expression
和 Pattern
相关的内容的。
PrivateName
interface PrivateName <: Expression, Pattern { type: "PrivateName"; id: Identifier; } 复制代码
A Private Name Identifier.
Literal
字面量,这里不是指 []
或者 {}
这些,而是本身语义就代表了一个值的字面量,如 1
, “hello”
, true
这些,还有正则表达式(有一个扩展的 Node
来表示正则表达式),如 /\d?/
。我们看一下文档的定义:
interface Literal <: Expression { type: "Literal"; value: string | boolean | null | number | RegExp; } 复制代码
RegExpLiteral
value
这里即对应了字面量的值,我们可以看出字面量值的类型,字符串,布尔,数值, null
和正则。
这个针对正则字面量的,为了更好地来解析正则表达式的内容,添加多一个 regex
字段,里边会包括正则本身,以及正则的 flags
。
interface RegExpLiteral <: Literal { regex: { pattern: string; flags: string; }; } 复制代码
Programs
一般这个是作为根节点的,即代表了一棵完整的程序代码树。
interface Program <: Node { type: "Program"; body: [ Statement ]; } 复制代码
body
属性是一个数组,包含了多个 Statement
(即语句)节点。
Functions
函数声明或者函数表达式节点。
interface Function <: Node { id: Identifier | null; params: [ Pattern ]; body: BlockStatement; } 复制代码
id
是函数名, params
属性是一个数组,表示函数的参数。 body
是一个块语句。
有一个值得留意的点是,你在测试过程中,是不会找到 type: "Function"
的节点的,但是你可以找到 type: "FunctionDeclaration"
和 type: "FunctionExpression"
,因为函数要么以声明语句出现,要么以函数表达式出现,都是节点类型的组合类型,后边会再提及 FunctionDeclaration
和 FunctionExpression
的相关内容。
Statement
语句节点没什么特别的,它只是一个节点,一种区分,但是语句有很多种,下边会详述。
interface Statement <: Node { } 复制代码
ExpressionStatement
表达式语句节点, a = a + 1
或者 a++
里边会有一个 expression
属性指向一个表达式节点对象(后边会提及表达式)。
interface ExpressionStatement <: Statement { type: "ExpressionStatement"; expression: Expression; } 复制代码
BlockStatement
块语句节点,举个例子: if (...) { // 这里是块语句的内容 }
,块里边可以包含多个其他的语句,所以有一个 body
属性,是一个数组,表示了块里边的多个语句。
interface BlockStatement <: Statement { type: "BlockStatement"; body: [ Statement ]; } 复制代码
ReturnStatement
返回语句节点, argument
属性是一个表达式,代表返回的内容。
interface ReturnStatement <: Statement { type: "ReturnStatement"; argument: Expression | null; } 复制代码
IfStatement
if
语句节点,很常见,会带有三个属性, test
属性表示 if (...)
括号中的表达式。
consequent
属性是表示条件为 true
时的执行语句,通常会是一个块语句。
alternate
属性则是用来表示 else
后跟随的语句节点,通常也会是块语句,但也可以又是一个 if
语句节点,即类似这样的结构: if (a) { //... } else if (b) { // ... }
。 alternate
当然也可以为 null
。
interface IfStatement <: Statement { type: "IfStatement"; test: Expression; consequent: Statement; alternate: Statement | null; } 复制代码
SwitchStatement
switch
语句节点,有两个属性, discriminant
属性表示 switch
语句后紧随的表达式,通常会是一个变量, cases
属性是一个 case
节点的数组,用来表示各个 case
语句。
interface SwitchStatement <: Statement { type: "SwitchStatement"; discriminant: Expression; cases: [ SwitchCase ]; } 复制代码
ForStatement
for
循环语句节点,属性 init/test/update
分别表示了 for
语句括号中的三个表达式,初始化值,循环判断条件,每次循环执行的变量更新语句( init
可以是变量声明或者表达式)。这三个属性都可以为 null
,即 for(;;){}
。 body
属性用以表示要循环执行的语句。
interface ForStatement <: Statement { type: "ForStatement"; init: VariableDeclaration | Expression | null; test: Expression | null; update: Expression | null; body: Statement; } 复制代码
Declarations
声明语句节点,同样也是语句,只是一个类型的细化。下边会介绍各种声明语句类型。
interface Declaration <: Statement { } 复制代码
FunctionDeclaration
函数声明,和之前提到的 Function 不同的是, id
不能为 null
。
interface FunctionDeclaration <: Function, Declaration { type: "FunctionDeclaration"; id: Identifier; } 复制代码
VariableDeclaration
变量声明, kind
属性表示是什么类型的声明,因为 ES6 引入了 const/let
。 declarations
表示声明的多个描述,因为我们可以这样: let a = 1, b = 2;
。
interface VariableDeclaration <: Declaration { type: "VariableDeclaration"; declarations: [ VariableDeclarator ]; kind: "var"; } 复制代码
VariableDeclarator
变量声明的描述, id
表示变量名称节点, init
表示初始值的表达式,可以为 null
。
interface VariableDeclarator <: Node { type: "VariableDeclarator"; id: Pattern; init: Expression | null; } 复制代码
Expressions
表达式节点。
interface Expression <: Node { } 复制代码
Import
interface Import <: Node { type: "Import"; } 复制代码
ArrayExpression
数组表达式节点, elements
属性是一个数组,表示数组的多个元素,每一个元素都是一个表达式节点。
interface ArrayExpression <: Expression { type: "ArrayExpression"; elements: [ Expression | null ]; } 复制代码
ObjectExpression
对象表达式节点, property
属性是一个数组,表示对象的每一个键值对,每一个元素都是一个属性节点。
interface ObjectExpression <: Expression { type: "ObjectExpression"; properties: [ Property ]; } 复制代码
Property
对象表达式中的属性节点。 key
表示键, value
表示值,由于 ES5 语法中有 get/set
的存在,所以有一个 kind
属性,用来表示是普通的初始化,或者是 get/set
。
interface Property <: Node { type: "Property"; key: Literal | Identifier; value: Expression; kind: "init" | "get" | "set"; } 复制代码
FunctionExpression
函数表达式节点。
interface FunctionExpression <: Function, Expression { type: "FunctionExpression"; } 复制代码
BinaryExpression
二元运算表达式节点, left
和 right
表示运算符左右的两个表达式, operator
表示一个二元运算符。
interface BinaryExpression <: Expression { type: "BinaryExpression"; operator: BinaryOperator; left: Expression; right: Expression; } 复制代码
BinaryOperator
二元运算符,所有值如下:
enum BinaryOperator { "==" | "!=" | "===" | "!==" | "<" | "<=" | ">" | ">=" | "<<" | ">>" | ">>>" | "+" | "-" | "*" | "/" | "%" | "|" | "^" | "&" | "in" | "instanceof" } 复制代码
AssignmentExpression
赋值表达式节点, operator
属性表示一个赋值运算符, left
和 right
是赋值运算符左右的表达式。
interface AssignmentExpression <: Expression { type: "AssignmentExpression"; operator: AssignmentOperator; left: Pattern | Expression; right: Expression; } 复制代码
AssignmentOperator
赋值运算符,所有值如下:(常用的并不多)
enum AssignmentOperator { "=" | "+=" | "-=" | "*=" | "/=" | "%=" | "<<=" | ">>=" | ">>>=" | "|=" | "^=" | "&=" } 复制代码
ConditionalExpression
条件表达式,通常我们称之为三元运算表达式,即 boolean ? true : false
。属性参考条件语句。
interface ConditionalExpression <: Expression { type: "ConditionalExpression"; test: Expression; alternate: Expression; consequent: Expression; } 复制代码
Misc
Decorator
interface Decorator <: Node { type: "Decorator"; expression: Expression; } 复制代码
Patterns
模式,主要在 ES6 的解构赋值中有意义,在 ES5 中,可以理解为和 Identifier
差不多的东西。
interface Pattern <: Node { } 复制代码
Classes
interface Class <: Node { id: Identifier | null; superClass: Expression | null; body: ClassBody; decorators: [ Decorator ]; } 复制代码
ClassBody
interface ClassBody <: Node { type: "ClassBody"; body: [ ClassMethod | ClassPrivateMethod | ClassProperty | ClassPrivateProperty ]; } 复制代码
ClassMethod
interface ClassMethod <: Function { type: "ClassMethod"; key: Expression; kind: "constructor" | "method" | "get" | "set"; computed: boolean; static: boolean; decorators: [ Decorator ]; } 复制代码
Modules
ImportDeclaration
interface ImportDeclaration <: ModuleDeclaration { type: "ImportDeclaration"; specifiers: [ ImportSpecifier | ImportDefaultSpecifier | ImportNamespaceSpecifier ]; source: Literal; } 复制代码
import 声明,如: import foo from "mod";
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- 前端_JavaScript_语法篇
- 前端的flutter之路(一):语法
- 胡子哥的重学前端(笔记)css语法
- 重学前端学习笔记(十)--CSS语法关于带@的规则
- Swift语法快速入门(一)之 Swift基础语法
- 在ES6中使用扩展语法有什么好处?它与rest语法有什么不同?
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
颠覆者:周鸿祎自传
周鸿祎、范海涛 / 北京联合出版公司 / 2017-11 / 49.80元
周鸿祎,一个在中国互联网历史上举足轻重的名字。他被认为是奠定当今中国互联网格局的人之一。 作为第一代互联网人,中国互联网行业最好的产品经理、创业者,他每时每刻都以自己的实践,为互联网的发展贡献自己的力量。 在很长一段时间内,他没有在公共场合发声,甚至有粉丝对当前死水一潭的互联网现状不满意,发出了“人民想念周鸿祎”的呼声。 但周鸿祎在小时候,却是一个踢天弄井,动不动就大闹天宫的超级......一起来看看 《颠覆者:周鸿祎自传》 这本书的介绍吧!