精华内容
下载资源
问答
  • babel原理

    2019-08-31 20:21:32
    什么是BabelBabel 能够转译 ECMAScript 2015+ 的代码,使它在旧的浏览器或者环境中也能够运行。 Babel 大概分为三大部分: 解析: 将代码(其实就是字符串)转换成 AST( 抽象语法树) 转换: 访问 AST 的节点进行...

    什么是Babel?

    Babel 能够转译 ECMAScript 2015+ 的代码,使它在旧的浏览器或者环境中也能够运行。

    Babel 大概分为三大部分:

    • 解析: 将代码(其实就是字符串)转换成 AST( 抽象语法树)
    • 转换: 访问 AST 的节点进行变换操作生成新的 AST
    • 生成: 以新的 AST 为基础生成代码

    抽象语法树(AST)
    举个例子:

    // JS
    const add = (a,b) => a+b;
    
    // 上面代码生成的AST
    {
      "type": "Program",
      "body": [
        {
          "type": "VariableDeclaration", // 变量声明
          "declarations": [ // 具体声明
            {
              "type": "VariableDeclarator", // 变量声明
              "id": {
                "type": "Identifier", // 标识符(最基础的)
                "name": "add" // 函数名
              },
              "init": {
                "type": "ArrowFunctionExpression", // 箭头函数
                "id": null,
                "expression": true,
                "generator": false,
                "params": [ // 参数
                  {
                    "type": "Identifier",
                    "name": "a"
                  },
                  {
                    "type": "Identifier",
                    "name": "b"
                  }
                ],
                "body": { // 函数体
                  "type": "BinaryExpression", // 二项式
                  "left": { // 二项式左边
                    "type": "Identifier",
                    "name": "a"
                  },
                  "operator": "+", // 二项式运算符
                  "right": { // 二项式右边
                    "type": "Identifier",
                    "name": "b"
                  }
                }
              }
            }
          ],
          "kind": "const"
        }
      ],
      "sourceType": "module"
    }
    
    

    在这里插入图片描述AST explorer

    Babel工作流程

    1. Parse(解析)
      Parse阶段分为两个阶段:词法分析和语法分析
    • 词法分析:可以看成是对代码进行“分词”,它接收一段源代码,然后执行一段 tokenize 函数,把代码分割成被称为Tokens(令牌流)。
    [
        { "type": "Keyword", "value": "const" },
        { "type": "Identifier", "value": "add" },
        { "type": "Punctuator", "value": "=" },
        { "type": "Punctuator", "value": "(" },
        { "type": "Identifier", "value": "a" },
        { "type": "Punctuator", "value": "," },
        { "type": "Identifier", "value": "b" },
        { "type": "Punctuator", "value": ")" },
        { "type": "Punctuator", "value": "=>" },
        { "type": "Identifier", "value": "a" },
        { "type": "Punctuator", "value": "+" },
        { "type": "Identifier", "value": "b" }
    ]
    

    模拟实现

    function tokenize(code) {
        if (!code || code.length === 0) {
            return [];
        }
        var current = 0; // 记录位置
        var tokens = []; // 定义一个空的 token 数组
        
        var LETTERS = /[a-zA-Z\$\_]/i;
        var KEYWORDS = /const/; //  模拟一下判断是不是关键字
        var WHITESPACE = /\s/;
        var PARENS = /\(|\)/;
        var NUMBERS = /[0-9]/;
        var OPERATORS = /[+*/-]/;
        var PUNCTUATORS = /[~!@#$%^&*()/\|,.<>?"';:_+-=\[\]{}]/;
        
        // 从第一个字符开始遍历
        while (current < code.length) {
            var char = code[current];
            // 判断空格
            if (WHITESPACE.test(char)) {
              current++;
              continue;
            }
            // 判断连续字符
            if (LETTERS.test(char)) {
                var value = '';
                var type = 'Identifier';
                while (char && LETTERS.test(char)) {
                    value += char;
                    char = code[++current];
                }
                // 判断是否是关键字
                if (KEYWORDS.test(value)) {
                    type = 'Keyword'
                }
                tokens.push({
                    type: type,
                    value: value
                });
                continue;
            }
            // 判断小括号
            if (PARENS.test(char)) {
                tokens.push({
                  type: 'Paren',
                  value: char
                });
                current++;
                continue;
            }
            // 判断连续数字
            if (NUMBERS.test(char)) {
              var value = '';
              while (char && NUMBERS.test(char)) {
                value += char;
                char = code[++current];
              }
              tokens.push({
                type: 'Number',
                value: value
              });
              continue;
            }
            // 判断运算符
            if (OPERATORS.test(char)) {
                tokens.push({
                    type: 'Operator',
                    value: char
                });
                current++;
                continue;
            }
            // 判断箭头函数
            if (PUNCTUATORS.test(char)) {
                var value = char;
                var type = 'Punctuator';
                var temp = code[++current];
                if (temp === '>') {
                    type = 'ArrowFunction';
                    value += temp;
                    current ++;
                }
                tokens.push({
                    type: type,
                    value: value
                });
                continue;
            }
            tokens.push({
                type: 'Identifier',
                value: char
            });
            current++;
        }
        return tokens;
    }
    
    
    • 语法分析:会把一个令牌流转换成 AST 的形式。 这个阶段会使用令牌中的信息把它们转换成一个 AST 的结构,这样更易于后续的操作。语法分析需要开发者根据token流提供的信息来分析出代码之间的逻辑关系,只有经过词法分析 token 流才能成为有结构的抽象语法树.依据的标准为estree标准

    Babel 使用 @babel/parser 解析代码,输入的 js 代码字符串根据 ESTree 规范生成 AST(抽象语法树)。Babel 使用的解析器是 babylon。

    1. Transform(转换)
      转换的关键就是根据当前的抽象语法树,以我们定义的规则生成新的抽象语法树,转换的过程就是生成新抽象语法树的过程.
      Babel 对于 AST 的遍历是深度优先遍历,对于 AST 上的每一个分支 Babel 都会先向下遍历走到尽头,然后再向上遍历退出刚遍历过的节点,然后寻找下一个分支。
    const traverser = (ast, visitor) => {
    
        // 如果节点是数组那么遍历数组
        const traverseArray = (array, parent) => {
            array.forEach((child) => {
                traverseNode(child, parent);
            });
        };
    
        // 遍历 ast 节点
        const traverseNode = (node, parent) => {
            const method = visitor[node.type];
    
            if (method) {
                method(node, parent);
            }
    
            switch (node.type) {
            case 'Program':
                traverseArray(node.body, node);
                break;
    
            case 'VariableDeclaration':
                traverseArray(node.init.params, node.init);
                break;
    
            case 'identifier':
                break;
    
            default:
                throw new TypeError(node.type);
            }
        };
        traverseNode(ast, null);
    };
    
    

    Babel 会维护一个称作 Visitor 的对象,这个对象定义了用于 AST 中获取具体节点的方法。
    Babel提供了@babel/traverse(遍历)方法维护这AST树的整体状态,并且可完成对其的替换,删除或者增加节点,这个方法的参数为原始AST和自定义的转换规则,返回结果为转换后的AST。
    3. Generate(生成)
    根据我们转换后的抽象语法树来生成新的代码,我们会实现一个函数, 他接受一个对象( ast),通过递归生成最终的代码

    const generator = (node) => {
       switch (node.type) {
       // 如果是 `Program` 结点,那么我们会遍历它的 `body` 属性中的每一个结点,并且递归地
       // 对这些结点再次调用 codeGenerator,再把结果打印进入新的一行中。
       case 'Program':
           return node.body.map(generator)
               .join('\n');
    
       // 如果是FunctionDeclaration我们分别遍历调用其参数数组以及调用其 body 的属性
       case 'FunctionDeclaration':
           return 'function' + ' ' + node.identifierName + '(' + node.params.map(generator) + ')' + ' ' + generator(node.body);
    
       // 对于 `Identifiers` 我们只是返回 `node` 的 identifierName
       case 'identifier':
           return node.identifierName;
    
       // 如果是BlockStatement我们遍历调用其body数组
       case 'BlockStatement':
           return '{' + node.body.map(generator) + '}';
    
       // 如果是ReturnStatement我们调用其 argument 的属性
       case 'ReturnStatement':
           return 'return' + ' ' + generator(node.argument);
       
       // 如果是ReturnStatement我们调用其左右节点并拼接
       case 'BinaryExpression':
           return generator(node.left) + ' ' + node.operator + ' ' + generator(node.right);
    
       // 没有符合的则报错
       default:
           throw new TypeError(node.type);
    
       }
    };
    

    总结:

    1. babylon 将 ES6/ES7 代码解析成 AST
    2. @babel/traverse 对 AST 进行遍历转译,得到新的 AST
    3. 新 AST 通过@babel/generator 转换成 ES5
    展开全文
  • AST语法树和babel原理

    2020-12-10 10:37:14
    '+'){ return node.left.value + node.right.value } } 新建一个test.js,里面写入var a = 1 + 2 运行代码,可以看到after.js文件,里面是a = 3 这样我们就简易的写了个babel的实现原理。 其实babel也是这么做的,只...

    AST(抽象语法树)

    在计算机科学中,抽象语法树(abstract syntax tree 或者缩写为 AST),或者语法树(syntax tree),是源代码的抽象语法结构的树状表现形式,这里特指编程语言的源代码。树上的每个节点都表示源代码中的一种结构。

    无论是LL(1)文法,还是LR(1),或者还是其它的方法,都要求在语法分析时候,构造出相同的语法树,这样可以给编译器后端提供了清晰,统一的接口。即使是前端采用了不同的文法,都只需要改变前端代码,而不用连累到后端。即减少了工作量,也提高的编译器的可维护性。

    项目源码和更多案例放在github上面,欢迎star.


    抽象语法树有什么用呢?

    • IDE的错误提示、代码格式化、代码高亮、代码自动补全等
    • JSLint、JSHint、ESLint对代码错误或风格的检查等
    • webpack、rollup进行代码打包等
    • Babel 转换 ES6 到 ES5 语法
    • 注入代码统计单元测试覆盖率

    AST是如何生成的?

    能够将JavaScript源码转化为抽象语法树(AST)的工具叫做JS Parser解析器。
    JS Parser的解析过程包括两部分

    词法分析(Lexical Analysis):词法分析阶段把字符串形式的代码转换为 令牌(tokens) 流。
    语法分析(Syntax Analysis): 语法分析阶段会把一个令牌流转换成 AST 的形式。 这个阶段会使用令牌中的信息把它们转换成一个 AST 的表述结构,这样更易于后续的操作。

    你会留意到 AST 的每一层都拥有相同的结构:

    
    {
      type: "FunctionDeclaration",
      id: {...},
      params: [...],
      body: {...}
    }
    
    
    {
      type: "Identifier",
      name: ...
    }
    
    {
      type: "BinaryExpression",
      operator: ...,
      left: {...},
      right: {...}
    }
    

    手写一个简易babel转化。

    Babel 的三个主要处理步骤分别是: 解析(parse),转换(transform),生成(generate)。

    解析
    解析步骤接收代码并输出 AST。 这个步骤分为两个阶段:词法分析(Lexical Analysis)和 语法分析(Syntactic Analysis)

    转换

    转换步骤接收 AST 并对其进行遍历,在此过程中对节点进行添加、更新及移除等操作。 这是 Babel 或是其他编译器中最复杂的过程

    生成

    生成啊其实很简单:深度优先遍历整个 AST,然后构建可以表示转换后代码的字符串。

    我们做一个对加法进行处理的简单程序。

    首先先去网站https://astexplorer.net/ 得到var a = 1 + 1的AST语法树.

    {
      "type": "Program",
      "start": 0,
      "end": 11,
      "body": [
        {
          "type": "VariableDeclaration",
          "start": 0,
          "end": 11,
          "declarations": [
            {
              "type": "VariableDeclarator",
              "start": 4,
              "end": 11,
              "id": {
                "type": "Identifier",
                "start": 4,
                "end": 5,
                "name": "a"
              },
              "init": {
                "type": "BinaryExpression",
                "start": 8,
                "end": 11,
                "left": {
                  "type": "Literal",
                  "start": 8,
                  "end": 9,
                  "value": 1,
                  "raw": "1"
                },
                "operator": "+",
                "right": {
                  "type": "Literal",
                  "start": 10,
                  "end": 11,
                  "value": 1,
                  "raw": "1"
                }
              }
            }
          ],
          "kind": "var"
        }
      ],
      "sourceType": "module"
    }
    
    

    然后写babel代码。babel转化需要三步。(解析–转换–生成)

    //引入工具包
    const esprima = require('esprima');//JS语法树模块
    const estraverse = require('estraverse');//JS语法树遍历各节点
    const escodegen = require('escodegen');//JS语法树反编译模块
    const fs = require('fs');//读写文件
    
    
    const before = fs.readFileSync('./test.js', 'utf8');
    const ast = esprima.parseScript(before); // 解析
    
    estraverse.traverse(ast, { // 转换
      enter: (node) => {
        if(node.type == 'BinaryExpression'){
          var num = toEqual(node);//把 1+1 改成2
          Object.assign(node,{
            "type": "Literal",
            "value": num,
            "raw":num
          })
          
        }
      }
    });
    
    
    const code = escodegen.generate(ast); 
    //写入文件  生成
    fs.existsSync('./after.js') && fs.unlinkSync('./after.js');
    fs.writeFileSync('./after.js', code, 'utf8');
    
    
    function toEqual(node){
     if(node.operator == '+'){
       return node.left.value + node.right.value
     }
    }
    
    
    
    

    新建一个test.js,里面写入var a = 1 + 2

    运行代码,可以看到after.js文件,里面是a = 3

    这样我们就简易的写了个babel的实现原理。

    其实babel也是这么做的,只不过它的转换规则函数相当的复杂,因为需要考虑各种JavaScript的语法情况,工作量巨大,这也就是babel最核心的地方。


    a.b 和 a[‘b’] 谁的速度快?

    在V8引擎中,js从源代码到机器码的转译主要有三个步骤:Parser(AST) ->Ignition(Bytecode)->TurboFan(Machine Code)

    • Parser:负责将JavaScript源码转换为Abstract Syntax Tree (AST)
    • Ignition:interpreter,即解释器,负责将AST转换为Bytecode,解释执行Bytecode;同时收集TurboFan优化编译所需的信息,比如函数参数的类型
    • TurboFan:compiler,即编译器,利用Ignitio所收集的类型信息,将Bytecode转换为优化的汇编代码
    展开全文
  • Babel原理

    2020-02-13 11:30:16
    在学习es6的时候就一直希望了解一下bable的转码原理,偶然的机会查了查,整理了一下。 Bable的主要工作原理 Bable是一个转译器,与编译器不同。bable主要负责目标代码的版本转换,只处理同一种语言,并且只负责输出...

    前言

    在学习es6的时候就一直希望了解一下bable的转码原理,偶然的机会查了查,整理了一下。


    Bable的主要工作原理
    Bable是一个转译器,与编译器不同。bable主要负责目标代码的版本转换,只处理同一种语言,并且只负责输出转码后的代码串。大概分三个阶段:

    • parsing 生成AST抽象语法树(将整个代码字符串分割成语法单元 ,进行语义分析,在分词结果的基础之上分析语法单元之间的关系)
    • transforming 修改语法树,生成新的语法树
    • generating 根据新的语法树生成目标代码

    Babel主要由几个核心包组成

    • babel-core:babel转译器本身,提供了babel的转译API。webpack的babel-loader就是调用这些API来完成转译过程。
    • babylon:js的词法解析器
    • babel-traverse:用于对AST的遍历,主要给plugin用
    • babel-generator:根据AST生成代码

    Bable核心概念

    • plugins
      主要在第二阶段transforming,修改AST树,生成新的语法树时使用。bable会根据配置的plugins进行AST的修改。

    • presets
      presets是Bable为我们预设好的plugins,不需要自行配置。如es2015、es2016、es2017

    • polyfill
      babel-polyfill是一个ES2015+环境的shim,全局引入后,可以做到ES2015+代码的开箱即用。但缺点是其会污染全局环境。

    • runtime
      babel-runtime的作用是,当使用如es6的async函数、let声明等新原生对象、静态方法、函数时,用babel-runtime中导出的对象、静态方法、函数来替代。从而保证全局huan明天约到面试了就先不写了,开始准备咯!!!!!

    参考

    https://zhuanlan.zhihu.com/p/27289600
    https://www.jianshu.com/p/e9b94b2d52e2

    展开全文
  • 面试题之babel原理及使用

    千次阅读 2020-05-13 20:43:27
    在一些面试中有可能会问到babel工作原理这是为为了卡你薪资 当你回答上来的话那么你就牛逼了 简单的总结babel工作原理就是: ES6代码输入 ==》 babylon进行解析 》 得到AST》 plugin用babel-traverse对AST树进行遍历...

    在一些面试中有可能会问到babel工作原理这是为为了卡你薪资
    当你回答上来的话那么你就牛逼了
    简单的总结babel工作原理就是:

    ES6代码输入 ==》 babylon进行解析 》 得到AST》 plugin用babel-traverse对AST树进行遍历转译 ==》 得到新的AST树
    ==》 用babel-generator通过AST树生成ES5代码

    面试官想要听的就是这一部分 剩下的再跟他扯

    剩下这些了解一下就可以 网上大把
    参考这个网址 :https://www.jianshu.com/p/e9b94b2d52e2

    Babel的包构成

    核心包

    • babel-core:babel转译器本身,提供了babel的转译API,如babel.transform等,用于对代码进行转译。像webpack的babel-loader就是调用这些API来完成转译过程的。
    • babylon:js的词法解析器
    • babel-traverse:用于对AST(抽象语法树,想了解的请自行查询编译原理)的遍历,主要给plugin用
    • babel-generator:根据AST生成代码

    功能包

    • babel-types:用于检验、构建和改变AST树的节点
    • babel-template:辅助函数,用于从字符串形式的代码来构建AST树节点
    • babel-helpers:一系列预制的babel-template函数,用于提供给一些plugins使用
    • babel-code-frames:用于生成错误信息,打印出错误点源代码帧以及指出出错位置
    • babel-plugin-xxx:babel转译过程中使用到的插件,其中babel-plugin-transform-xxx是transform步骤使用的
    • babel-preset-xxx:transform阶段使用到的一系列的plugin
    • babel-polyfill:JS标准新增的原生对象和API的shim,实现上仅仅是core-js和regenerator-runtime两个包的封装
    • babel-runtime:功能类似babel-polyfill,一般用于library或plugin中,因为它不会污染全局作用域

    工具包

    • babel-cli:babel的命令行工具,通过命令行对js代码进行转译
    • babel-register:通过绑定node.js的require来自动转译require引用的js代码文件

    babel的配置

    使用形式

    如果是以命令行方式使用babel,那么babel的设置就以命令行参数的形式带过去;
    还可以在package.json里在babel字段添加设置;
    但是建议还是使用一个单独的.babelrc文件,把babel的设置都放置在这里,所有babel API的options(除了回调函数之外)都能够支持,具体的options见babel的API options文档

    常用options字段说明

    • env:指定在不同环境下使用的配置。比如production和development两个环境使用不同的配置,就可以通过这个字段来配置。env字段的从process.env.BABEL_ENV获取,如果BABEL_ENV不存在,则从process.env.NODE_ENV获取,如果NODE_ENV还是不存在,则取默认值"development"
    • plugins:要加载和使用的插件列表,插件名前的babel-plugin-可省略;plugin列表按从头到尾的顺序运行
    • presets:要加载和使用的preset列表,preset名前的babel-preset-可省略;presets列表的preset按从尾到头的逆序运行(为了兼容用户使用习惯)
    • 同时设置了presets和plugins,那么plugins的先运行;每个preset和plugin都可以再配置自己的option

    配置文件的查找

    babel会从当前转译的文件所在目录下查找配置文件,如果没有找到,就顺着文档目录树一层层往上查找,一直到.babelrc文件存在或者带babel字段的package.json文件存在为止。

    babel的工作原理

    babel是一个转译器,感觉相对于编译器compiler,叫转译器transpiler更准确,因为它只是把同种语言的高版本规则翻译成低版本规则,而不像编译器那样,输出的是另一种更低级的语言代码。
    但是和编译器类似,babel的转译过程也分为三个阶段:parsing、transforming、generating,以ES6代码转译为ES5代码为例,babel转译的具体过程如下:

    ES6代码输入 ==》 babylon进行解析 》 得到AST》 plugin用babel-traverse对AST树进行遍历转译 ==》 得到新的AST树
    ==》 用babel-generator通过AST树生成ES5代码

    此外,还要注意很重要的一点就是,babel只是转译新标准引入的语法,比如ES6的箭头函数转译成ES5的函数;而新标准引入的新的原生对象,部分原生对象新增的原型方法,新增的API等(如Proxy、Set等),这些babel是不会转译的。需要用户自行引入polyfill来解决

    plugins

    插件应用于babel的转译过程,尤其是第二个阶段transforming,如果这个阶段不使用任何插件,那么babel会原样输出代码。
    我们主要关注transforming阶段使用的插件,因为transform插件会自动使用对应的词法插件,所以parsing阶段的插件不需要配置。

    presets

    如果要自行配置转译过程中使用的各类插件,那太痛苦了,所以babel官方帮我们做了一些预设的插件集,称之为preset,这样我们只需要使用对应的preset就可以了。以JS标准为例,babel提供了如下的一些preset:

    • es2015
    • es2016
    • es2017
    • env
      es20xx的preset只转译该年份批准的标准,而env则代指最新的标准,包括了latest和es20xx各年份
      另外,还有 stage-0到stage-4的标准成形之前的各个阶段,这些都是实验版的preset,建议不要使用。

    polyfill

    polyfill是一个针对ES2015+环境的shim,实现上来说babel-polyfill包只是简单的把core-js和regenerator runtime包装了下,这两个包才是真正的实现代码所在(后文会详细介绍core-js)。
    使用babel-polyfill会把ES2015+环境整体引入到你的代码环境中,让你的代码可以直接使用新标准所引入的新原生对象,新API等,一般来说单独的应用和页面都可以这样使用。

    使用方法

    1. 先安装包: npm install --save babel-polyfill
    2. 要确保在入口处导入polyfill,因为polyfill代码需要在所有其他代码前先被调用
      代码方式: import “babel-polyfill”
      webpack配置: module.exports = { entry: [“babel-polyfill”, “./app/js”] };

    如果只是需要引入部分新原生对象或API,那么可以按需引入,而不必导入全部的环境,具体见下文的core-js

    runtime

    polyfill和runtime的区别

    直接使用babel-polyfill对于应用或页面等环境在你控制之中的情况来说,并没有什么问题。但是对于在library中使用polyfill,就变得不可行了。因为library是供外部使用的,但外部的环境并不在library的可控范围,而polyfill是会污染原来的全局环境的(因为新的原生对象、API这些都直接由polyfill引入到全局环境)。这样就很容易会发生冲突,所以这个时候,babel-runtime就可以派上用场了。

    transform-runtime和babel-runtime

    babel-plugin-transform-runtime插件依赖babel-runtime,babel-runtime是真正提供runtime环境的包;也就是说transform-runtime插件是把js代码中使用到的新原生对象和静态方法转换成对runtime实现包的引用,举个例子如下:

    // 输入的ES6代码
    var sym = Symbol();
    // 通过transform-runtime转换后的ES5+runtime代码 
    var _symbol = require("babel-runtime/core-js/symbol");
    var sym = (0, _symbol.default)();
    

    从上面这个例子可见,原本代码中使用的ES6新原生对象Symbol被transform-runtimec插件转换成了babel-runtime的实现,既保持了Symbol的功能,同时又没有像polyfill那样污染全局环境(因为最终生成的代码中,并没有对Symbol的引用)
    另外,这里我们也可以隐约发现,babel-runtime其实也不是真正的实现代码所在,真正的代码实现是在core-js中,后面我们再说

    transform-runtime插件的功能

    1. 把代码中的使用到的ES6引入的新原生对象和静态方法用babel-runtime/core-js导出的对象和方法替代
    2. 当使用generators或async函数时,用babel-runtime/regenerator导出的函数取代(类似polyfill分成regenerator和core-js两个部分)
    3. 把Babel生成的辅助函数改为用babel-runtime/helpers导出的函数来替代(babel默认会在每个文件顶部放置所需要的辅助函数,如果文件多的话,这些辅助函数就在每个文件中都重复了,通过引用babel-runtime/helpers就可以统一起来,减少代码体积)

    上述三点就是transform-runtime插件所做的事情,由此也可见,babel-runtime就是一个提供了regenerator、core-js和helpers的运行时库。
    建议不要直接使用babel-runtime,因为transform-runtime依赖babel-runtime,大部分情况下都可以用transform-runtime达成目的。
    此外,transform-runtime在.babelrc里配置的时候,还可以设置helpers、polyfill、regenerator这三个开关,以自行决定runtime是否要引入对应的功能。
    最后补充一点:由于runtime不会污染全局空间,所以实例方法是无法工作的(因为这必须在原型链上添加这个方法,这是和polyfill最大的不同) ,比如:

    var arr = ['a', 'b', 'c'];
    arr.fill(7);  // 实例方法不行
    Array.prototype.fill.apply(arr, 7);  // 用原型链来调用也是不行
    

    通过core-js实现按需引入polyfill或runtime

    core-js包才上述的polyfill、runtime的核心,因为polyfill和runtime其实都只是对core-js和regenerator的再封装,方便使用而已。
    但是polyfill和runtime都是整体引入的,不能做细粒度的调整,如果我们的代码只是用到了小部分ES6而导致需要使用polyfill和runtime的话,会造成代码体积不必要的增大(runtime的影响较小)。所以,按需引入的需求就自然而然产生了,这个时候就得依靠core-js来实现了。

    core-js的组织结构

    首先,core-js有三种使用方式:

    • 默认方式:require(‘core-js’)
      这种方式包括全部特性,标准的和非标准的
    • 库的形式: var core = require(‘core-js/library’)
      这种方式也包括全部特性,只是它不会污染全局名字空间
    • 只是shim: require(‘core-js/shim’)或var shim = require(‘core-js/library/shim’)
      这种方式只包括标准特性(就是只有polyfill功能,没有扩展的特性)

    core-js的结构是高度模块化的,它把每个特性都组织到一个小模块里,然后再把这些小模块组合成一个大特性,层层组织。比如:
    core-js/es6(core-js/library/es6)就包含了全部的ES6特性,而core-js/es6/array(core-js/library/es6/array)则只包含ES6的Array特性,而core-js/fn/array/from(core-js/library/fn/array/from)则只有Array.from这个实现。
    实现按需使用,就是自己选择使用到的特性,然后导入即可。具体的每个特性和对应的路径可以直接查看core-js的github

    core-js的按需使用

    1、类似polyfill,直接把特性添加到全局环境,这种方式体验最完整

    require('core-js/fn/set');
    require('core-js/fn/array/from');
    require('core-js/fn/array/find-index');
    
    Array.from(new Set([1, 2, 3, 2, 1])); // => [1, 2, 3]
    [1, 2, NaN, 3, 4].findIndex(isNaN);   // => 2
    

    2、类似runtime一样,以库的形式来使用特性,这种方式不会污染全局名字空间,但是不能使用实例方法

    var Set       = require('core-js/library/fn/set');
    var from      = require('core-js/library/fn/array/from');
    var findIndex = require('core-js/library/fn/array/find-index');
    
    from(new Set([1, 2, 3, 2, 1]));      // => [1, 2, 3]
    findIndex([1, 2, NaN, 3, 4], isNaN); // => 2
    

    3、因为第二种库的形式不能使用prototype方法,所以第三种方式使用了一个小技巧,通过::这个符号而不是.来调用实例方式,从而达到曲线救国的目的。这种方式的使用,路径中都会带有/virtual/

    import {fill, findIndex} from 'core-js/library/fn/array/virtual';
    
    Array(10)::fill(0).map((a, b) => b * b)::findIndex(it => it && !(it % 8)); // => 4
    
    // 对比下polyfill的实现 
    // Array(10).fill(0).map((a, b) => b * b).findIndex(it => it && !(it % 8));
    

    总结

    Babel使用的难点主要在于理解polyfill、runtime和core-js,通过本文,把这三者的概念和关系理清楚了,对babel的使用就不存在问题!

    展开全文
  • 深入理解Babel原理及其使用

    千次阅读 2019-05-03 15:56:17
    babel-core:babel转译器本身,提供了babel的转译API,如babel.transform等,用于对代码进行转译。像webpack的babel-loader就是调用这些API来完成转译过程的。 babylon:js的词法解析器 babel-traverse:...
  • 更新 babel7.x preset 的变更:淘汰 es201x、删除 stage-x、强推 env npm package 名称的变化:把所有 babel-* 重命名为 @babel/* 不再支持低版本 node:要求 nodejs>=6 only 和 ignore 匹配规则的变化 @babel/...
  • Babel 原理

    2020-08-07 09:34:21
    本质就是编译器,当代码转为字符串生成 AST,对 AST 进行转变最后再生成新的代码 分为三步:词法分析生成 Token,语法分析生成 AST,遍历 AST,根据插件变换相应的节点,最后把 AST转换为代码 ...
  • Babel 为当前最流行的代码 JavaScript 编译器了,主要用于将 ECMAScript 2015+ 版本的代码转换为低版本兼容的 JavaScript 语法(比如箭头函数)。大名鼎鼎的 Taro 也是利用 Babel 将 JSX 语法转化成小程序模板。本 ...
  • babel 原理与演进

    千次阅读 2021-08-19 23:05:52
    什么是 babel 官网上的定义是 babel 是一个 JavaScript 编译器,具体来说,babel 是一个工具用于将 ECMAScript 2015+ 语法编写的代码转化成向后兼容的 JavaScript 语法,以便于能够运行在当前或者旧版本浏览器或其他...
  • Babel原理是什么?

    2021-03-22 15:41:02
    babel的转义过程也分为三个阶段,这三步具体是: 解析Parse:将代码解析生成抽象语法树(即AST),即词法分析与语法分析的过程 转换Transform:对于AST进行变换一系列的操作,babel接收得到AST并通过babel-traverse对...
  • babel 的配置 使用形式 命令行:babel 的设置以命令行参数的形式 package.json:在 babel 字段添加设置 .babelrc 文件:推荐使用 常用 options 字段说明 env: 指定在不同环境下使用的配置。env 字段从 process....
  • babel原理及插件开发

    2018-05-09 14:18:36
    大部分前端开发人员只是配置一下babel,根据需要装个插件之类,我想肯定少有人去研究babel转换ES6代码的原理及插件原理,于是在某个日子里由于项目的需要去研究了一下babel原理。 需要说明的是,本文不涉及ba...
  • babel 的包的构成 核心包 babel-core: babel 转译器本身,提供了 babal 的转译 API,如 babel.transform 等,用于对代码进行转译 babylon: js 的词法解析器(babel 7.x 重命名为 @babel/parser) babel-traverse: ...
  • babel原理和插件编写

    2019-07-01 11:27:10
    主要将js代码转化为浏览器或其他特定环境下的代码,比如转换为es5格式,其主要原理是ASTs(抽象语法树),babel在处理代码的每一步都涉及或创建语法树。 什么是AST: AST(Abstract Syntax Tree) 抽象语法树 /ˈæbstr...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 11,021
精华内容 4,408
关键字:

babel原理