美文网首页每日一题Web前端之路让前端飞
第3题-ES6代码转化为ES5的思路及实践

第3题-ES6代码转化为ES5的思路及实践

作者: 青天诀 | 来源:发表于2019-08-11 20:23 被阅读0次

    面试题目:

    ES6 代码转成 ES5 代码的实现思路是什么?

    示例:

    // 转换前
    const fn = () => {
        console.log('前端名狮');
    };
    
    // 转换后
    "use strict";
    var fn = function fn() {
        console.log('前端名狮');
    };
    

    答案解析:

    ES6作为JS的新规范,加入了很多新的语法和API,而市面上的浏览器并没有全部兼容,所以需要将ES6语法代码转为ES5的代码。

    es6比es5多出来的部分可分为两类:

    1. 一类是语法,如箭头函数,解构;
    2. 一类是新的类、新的类方法、新的实例方法,如:Promise, Array.from, Array.prototype.find

    转换语法时,一般是在抽象语法树层转换代码,如上文示例中的箭头函数。

    对于新的类、类方法,实例方法,基本就是用polyfill,或者polyfill加上代码转换。

    例如:Array.from,只需要使用es5的语法,自己实现一遍Array.from,就可以用不改动源代码而使用Array.from这个es6的api了。

    ES6 转 ES5 目前行业标配是用 Babel,转换的大致流程如下:

    1. 解析:解析代码字符串,生成 AST;
    2. 转换:按一定的规则转换、修改 AST;
    3. 生成:将修改后的 AST 转换成普通代码。

    下面来点干货,按照上面三个步骤

    实现:将 let 转换为 var

    // example.js
    
    let a = 3; 
    let b = 2;
    console.log(a + b);
    
    
    
    // app.js
    
    const fs = require('fs');
    const acorn = require('acorn'); //将JS代码转化为语法树模块
    const walk = require('acorn-walk'); //JS语法树遍历各节点
    const escodegen = require('escodegen'); //将JS语法树反编译成js代码模块
    
    
    // 1. 获取JS代码
    let code = fs.readFileSync('./example.js');
    // 2. 用acorn将代码解析为语法树ast
    let ast = acorn.parse(code, {
        ranges: true
    });
    // 3. 用walk操作语法树ast,输出node.value
    walk.simple(ast, {
        VariableDeclaration(node) {
            if(node.kind === 'let'){  
                node.kind = 'var'; // 把let 变成 var
            }
        }
    })
    
    fs.writeFileSync('result.json', JSON.stringify(ast));  // 将修改后的语法树ast存储为result.json文件
    fs.writeFileSync('result.js', escodegen.generate(ast, {comment: true})); // 用escodegen将语法树转换为最终代码,并存储为result.js
    

    输出结果

    // result.js
    
    var a = 3;
    var b = 2;
    console.log(a + b);
    
    
    // result.json  传说中的语法树
    
    {
        "type": "Program",
        "start": 0,
        "end": 44,
        "range": [0, 44],
        "body": [{
            "type": "VariableDeclaration",
            "start": 0,
            "end": 10,
            "range": [0, 10],
            "declarations": [{
                "type": "VariableDeclarator",
                "start": 4,
                "end": 9,
                "range": [4, 9],
                "id": {
                    "type": "Identifier",
                    "start": 4,
                    "end": 5,
                    "range": [4, 5],
                    "name": "a"
                },
                "init": {
                    "type": "Literal",
                    "start": 8,
                    "end": 9,
                    "range": [8, 9],
                    "value": 3,
                    "raw": "3"
                }
            }],
            "kind": "var"
        }, {
            "type": "VariableDeclaration",
            "start": 13,
            "end": 23,
            "range": [13, 23],
            "declarations": [{
                "type": "VariableDeclarator",
                "start": 17,
                "end": 22,
                "range": [17, 22],
                "id": {
                    "type": "Identifier",
                    "start": 17,
                    "end": 18,
                    "range": [17, 18],
                    "name": "b"
                },
                "init": {
                    "type": "Literal",
                    "start": 21,
                    "end": 22,
                    "range": [21, 22],
                    "value": 2,
                    "raw": "2"
                }
            }],
            "kind": "var"
        }, {
            "type": "ExpressionStatement",
            "start": 25,
            "end": 44,
            "range": [25, 44],
            "expression": {
                "type": "CallExpression",
                "start": 25,
                "end": 43,
                "range": [25, 43],
                "callee": {
                    "type": "MemberExpression",
                    "start": 25,
                    "end": 36,
                    "range": [25, 36],
                    "object": {
                        "type": "Identifier",
                        "start": 25,
                        "end": 32,
                        "range": [25, 32],
                        "name": "console"
                    },
                    "property": {
                        "type": "Identifier",
                        "start": 33,
                        "end": 36,
                        "range": [33, 36],
                        "name": "log"
                    },
                    "computed": false
                },
                "arguments": [{
                    "type": "BinaryExpression",
                    "start": 37,
                    "end": 42,
                    "range": [37, 42],
                    "left": {
                        "type": "Identifier",
                        "start": 37,
                        "end": 38,
                        "range": [37, 38],
                        "name": "a"
                    },
                    "operator": "+",
                    "right": {
                        "type": "Identifier",
                        "start": 41,
                        "end": 42,
                        "range": [41, 42],
                        "name": "b"
                    }
                }]
            }
        }],
        "sourceType": "script"
    }
    

    结论:

    1. result.json 就是修改后生成的语法树,json格式。
    2. 语法树中的kind的值由let被替换为var

    知识点延伸:

    Acorn

    acorn是常用的一个 JS 解析器,常用到的还有一个 Esprima,acorn在它之后诞生,两者相比,acorn实现代码更少,速度和Esprima相差无几。

    二者都遵循 The Estree Spec 规范,也就是得到的结果在很大部分上是兼容的。对 ECMAScript 来说,社区有一个 AST 规范::ESTree Spec

    解析 javascript 的三件套: acornacorn-walkescodegen

    AST 节点是按照如下的格式定义的:

    interface Node {
        type: string;
        loc: SourceLocation | null;
    }
    

    应用举例:

    1. webpack是使用acorn作为自己的Parser的基础库。

    2. babel, babylon.js也是fork的acorn实现的.

    最后推荐一个在线实时查看AST的网站:https://astexplorer.net/


    扫一扫 下方二维码,关注我的公众号【前端名狮】,更多精彩内容陪伴你!

    【前端名狮】

    相关文章

      网友评论

        本文标题:第3题-ES6代码转化为ES5的思路及实践

        本文链接:https://www.haomeiwen.com/subject/qxasjctx.html