我所认识到的Babel工作方式

301 阅读2分钟

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动

前言

在前端工程化中,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

  1. Parsing is taking raw code and turning it into a more abstract representation of the code.
  2. Transformation takes this abstract representation and manipulates to do whatever the compiler wants it to.
  3. Code Generation takes the transformed representation of the code and turns it into new code.

大多数编译器分为三个主要阶段: 解析转换代码生成

  1. 解析 是将原始代码转化为更抽象的代码表示形式 (AST)

通常分为两个阶段: 词法分析语法分析

  1. 转换 采用这种抽象表示并进行操作, 不管编译器想要什么 (AST TODO)
  2. 代码生成 采用转换后的代码表示形式,并将其转换为新的代码 (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的工作就是一个编译的过程,就是把上面几个过程串起来。

  1. input => tokenizer => tokens
  2. tokens => parser => ast
  3. ast => transformer => newAst
  4. 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;
}

结语

如果这篇文章帮到了你,欢迎点赞👍和关注⭐️。

文章如有错误之处,希望在评论区指正🙏🙏。

附: