小知识,大挑战!本文正在参与“程序员必备小知识”创作活动
前言
在前端工程化中,Babel
是一个必不可少的工具,主要用于将采用 ECMAScript 2015+
语法编写的代码转换为向后兼容的 JavaScript
语法,以便能够运行在当前和旧版本的浏览器或其他环境中。
Babel做了什么
- Transform syntax
- Polyfill features that are missing in your target environment (through a third-party polyfill such as core-js)
- Source code transformations (codemods)
- 语法转换
- 通过
Polyfill
方式在目标环境中添加缺失的特性(通过第三方polyfill
模块,例如 core-js,实现) - 源码转换 (
codemods
)
Babel 的工作方式(编译过程)
Most compilers break down into three primary stages: Parsing, Transformation and Code Generation
- Parsing is taking raw code and turning it into a more abstract representation of the code.
- Transformation takes this abstract representation and manipulates to do whatever the compiler wants it to.
- Code Generation takes the transformed representation of the code and turns it into new code.
大多数编译器分为三个主要阶段: 解析、转换和代码生成
- 解析 是将原始代码转化为更抽象的代码表示形式 (
AST
)
通常分为两个阶段: 词法分析和语法分析
- 转换 采用这种抽象表示并进行操作, 不管编译器想要什么 (
AST TODO
) - 代码生成 采用转换后的代码表示形式,并将其转换为新的代码 (
CODE
)
Babel
的是实现分为以下几个阶段:
tokenizer(分词器,称为词法分析过程)
通过词法分析,把代码串分解成一个令牌数组
function tokenizer(input) {
let current = 0;
let tokens = [];
while (current < input.length) {
let char = input[current];
if (char === '(') {
tokens.push({
type: 'paren',
value: '(',
});
// Then we increment `current`
current++;
// And we `continue` onto the next cycle of the loop.
continue;
}
if (char === ')') {
tokens.push({
type: 'paren',
value: ')',
});
current++;
continue;
}
let WHITESPACE = /\s/;
if (WHITESPACE.test(char)) {
current++;
continue;
}
let NUMBERS = /[0-9]/;
if (NUMBERS.test(char)) {
let value = '';
while (NUMBERS.test(char)) {
value += char;
char = input[++current];
}
tokens.push({ type: 'number', value });
continue;
}
if (char === '"') {
// Keep a `value` variable for building up our string token.
let value = '';
// We'll skip the opening double quote in our token.
char = input[++current];
// Then we'll iterate through each character until we reach another
// double quote.
while (char !== '"') {
value += char;
char = input[++current];
}
// Skip the closing double quote.
char = input[++current];
// And add our `string` token to the `tokens` array.
tokens.push({ type: 'string', value });
continue;
}
let LETTERS = /[a-z]/i;
if (LETTERS.test(char)) {
let value = '';
// Again we're just going to loop through all the letters pushing them to
// a value.
while (LETTERS.test(char)) {
value += char;
char = input[++current];
}
// And pushing that value as a token with the type `name` and continuing.
tokens.push({ type: 'name', value });
continue;
}
// Finally if we have not matched a character by now, we're going to throw
// an error and completely exit.
throw new TypeError('I dont know what this character is: ' + char);
}
// Then at the end of our `tokenizer` we simply return the tokens array.
return tokens;
}
parser(解析)
解析的过程,就是将获取标记数组并将其转换为AST
源码查看: the-super-tiny-compiler.js parser()
traverser (我称它为 遍历)
在这个阶段遍历AST
,访问不同节点,通过AST
类型匹配,然后调用不同节点的访问器。
源码查看: the-super-tiny-compiler.js traverser()
transformer(转换)
遍历AST
后,把AST
转为一个新的AST
语法树。
源码查看: the-super-tiny-compiler.js transformer()
codeGenerator (代码生成)
代码生成器将递归地调用自己,将树中的每个节点生成成一个完整的字符串 源码查看: the-super-tiny-compiler.js codeGenerator()
compiler(编译)
Babel
的工作就是一个编译的过程,就是把上面几个过程串起来。
input
=>tokenizer
=>tokens
tokens
=>parser
=>ast
ast
=>transformer
=>newAst
newAst
=>generator
=>output
源码如下:
function compiler(input) {
let tokens = tokenizer(input);
let ast = parser(tokens);
let newAst = transformer(ast);
let output = codeGenerator(newAst);
// and simply return the output!
return output;
}
结语
如果这篇文章帮到了你,欢迎点赞👍和关注⭐️。
文章如有错误之处,希望在评论区指正🙏🙏。
附: