Question 3 - The Idea and Practice of Converting ES6 Code into ES5

Posted by dourvas on Tue, 13 Aug 2019 05:59:15 +0200

Interview topics:

What is the idea of converting ES6 code to ES5 code?

Examples:

// Before conversion
const fn = () => {
    console.log('The Lion of the Front End');
};

// After conversion
"use strict";
var fn = function fn() {
    console.log('The Lion of the Front End');
};

Answer analysis:

As a new specification of JS, ES6 adds many new grammars and API s, while browsers on the market are not fully compatible, so it is necessary to convert ES6 grammar code to ES5 code.

The extra parts of es6 over es5 can be divided into two categories:

  1. One is grammar, such as arrow function, deconstruction.
  2. One is new class, new class method and new instance method, such as Promise, Array.from, Array.prototype.find.

When converting grammar, code is usually converted at the abstract grammar tree level, such as the arrow function in the example above.

For new class and class methods, instance methods are basically polyfill, or Polyfill plus code conversion.

For example: Array.from, you can use the es6 api of Array.from without changing the source code by using the es5 grammar and implementing it yourself.

ES6 to ES5 currently uses Babel as the industry standard. The general process of conversion is as follows:

  1. Parsing: Parsing code strings to generate AST;
  2. Conversion: Convert and modify AST according to certain rules;
  3. Generation: Converts the modified AST into normal code.

Here's some dry goods. Follow the three steps above.

Implementation: Convert let to var.

// example.js

let a = 3; 
let b = 2;
console.log(a + b);



// app.js

const fs = require('fs');
const acorn = require('acorn'); //Converting JS code into a syntax tree module
const walk = require('acorn-walk'); //JS syntax tree traverses nodes
const escodegen = require('escodegen'); //Decomposition of JS syntax tree into JS code module


// 1. Get the JS code
let code = fs.readFileSync('./example.js');
// 2. Using acorn to parse the code into a syntax tree ast
let ast = acorn.parse(code, {
    ranges: true
});
// 3. Use walk to manipulate the syntax tree ast and output node.value.
walk.simple(ast, {
    VariableDeclaration(node) {
        if(node.kind === 'let'){  
            node.kind = 'var'; // Change let into var
        }
    }
})

fs.writeFileSync('result.json', JSON.stringify(ast));  // Store the modified syntax tree ast as a result.json file
fs.writeFileSync('result.js', escodegen.generate(ast, {comment: true})); // Converting the syntax tree to the final code with escodegen and storing it as result.js

Output results

// result.js

var a = 3;
var b = 2;
console.log(a + b);


// The Grammar Tree in the Legend of 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"
}

Conclusion:

  1. result.json is the modified grammar tree, JSON format.
  2. The value of kind in the grammar tree is replaced by let with var.

Knowledge Point Extension:

Acorn

Acorn is a commonly used JS parser, and an Esprima is commonly used. Acorn was born after it. Compared with the two, acorn implements fewer code and has almost the same speed as Esprima.

Both follow The Estree Spec specification, i.e., the results are mostly compatible. For ECMAScript, the community has an AST specification: ESTree Spec

Three sets of javascript parsing: acorn, acorn-walk, escodegen.

AST nodes are defined in the following format:

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

Application examples:

  1. webpack is the base library of using acorn as its own Parser.

  2. babel, babylon.js are also implemented by fork's acorn.

Finally, an online real-time viewing AST website is recommended. https://astexplorer.net/

Sweep down the two-dimensional code, pay attention to my public number [front-end lion], more wonderful content to accompany you!

Topics: JSON ECMAScript Javascript Webpack