BabelJS - Transpile ES6 features to ES5

In this chapter, we will see the features added to ES6. We will also learn how to compile the features to ES5 using BabelJS.
Following are the various ES6 features that we will discuss in this chapter −
Let + Const
Arrow Functions
Classes
Promises
Generators
Destructuring
Iterators
Template Literalst
Enhanced Object
Default, Rest & Spread Properties
Let declares a block scope local variable in JavaScript. Consider the following example to understand the use of let.
let a = 1;
if (a == 1) {
   let a = 2;
   console.log(a);
}
console.log(a);
2
1

The reason the first console prints 2 is because a is declared again using let and will be available only in the if block. Any variable declared using let is just available within the declared block. We have declared variable a twice using let, but it does not overwrite the value of a.
This is the difference between var and let keywords. When you declare variable using var, the variable will be available within the scope of the function or if declared will act like a global variable.
Incase a variable is declared with let, the variable is available within the block scope. If declared inside the if statement, it will be available only within the if block. The same applies to switch, for-loop, etc.
We will now see the code conversion in ES5 using babeljs.
Let us run the following command to convert the code −
npx babel let.js --out-file let_es5.js

The output from es6 to es5 for the let keyword is as follows −
let a = 1;
if (a == 1) {
   let a = 2;
   console.log(a);
}
console.log(a);
"use strict";

var a = 1;
if (a == 1) {
   var _a = 2;
   console.log(_a);
}
console.log(a);
If you see the ES5 code the let keyword is replaced with the var keyword. Also the variable inside the if block is renamed to _a to have the same effect as when declared with the let keyword.
In this section, we will learn about the working of const keyword in ES6 and ES5. Const keyword is also available within the scope; and if outside, it will throw an error. The value of const declared variable cannot be changed once assigned. Let us consider the following example to understand how const keyword is used.
let a =1;
if (a == 1) {
   const age = 10;
}
console.log(age);
Uncaught ReferenceError: age is not defined at :5:13

The above output throws an error as the const age is defined inside the if block and is available within the if block.
We will understand the conversion to ES5 using BabelJS.
let a =1;
if (a == 1) {
   const age = 10;
}
console.log(age);
npx babel const.js --out-file const_es5.js

"use strict";

var a = 1;
if (a == 1) {
   var _age = 10;
}
console.log(age);
Incase of ES5, const keyword is replaced with the var keyword as shown above.
An Arrow function has a shorter syntax in comparison to the variable expression. it is also called the fat arrow function or lambda function. The function does not have its own this property. In this function, the keyword function is omitted.
var add = (x,y) => {
   return x+y;
}

var k = add(3,6);
console.log(k);
9

Using BabelJS, we will transpile the above code to ES5.
var add = (x,y) => {
   return x+y;
}

var k = add(3,6);
console.log(k);
npx babel arrowfunction.js --out-file arrowfunction_es5.js

Using Babel the arrow function is converted to variable expression function as shown below.
"use strict";

var add = function add(x, y) {
   return x + y;
};

var k = add(3, 6);
console.log(k);
ES6 comes with the new Classes feature. Classes are similar to the prototype based inheritance available in ES5.The class keyword is used to define the class. Classes are like special functions and have similarities like function expression. It has a constructor, which is called inside the class.
class Person {
   constructor(fname, lname, age, address) {
      this.fname = fname;
      this.lname = lname;
      this.age = age;
      this.address = address;
   }

   get fullname() {
      return this.fname +"-"+this.lname;
   }
}
var a = new Person("Siya", "Kapoor", "15", "Mumbai");
var persondet = a.fullname;
Siya-Kapoor

class Person {
   constructor(fname, lname, age, address) {
      this.fname = fname;
      this.lname = lname;
      this.age = age;
      this.address = address;
   }

   get fullname() {
      return this.fname +"-"+this.lname;
   }
}
var a = new Person("Siya", "Kapoor", "15", "Mumbai");
var persondet = a.fullname;
npx babel class.js --out-file class_es5.js

There is extra code added using babeljs to get the functionality working for classes same as in ES5.BabelJs makes sure the functionality works same as it would have done in ES6.
"use strict";

var _createClass = function () {
   function defineProperties(target, props) {
      for (var i = 0; i < props.length; i++) {
         var descriptor = props[i];
         descriptor.enumerable = descriptor.enumerable || false;
         descriptor.configurable = true;
         if ("value" in descriptor) descriptor.writable = true;
         Object.defineProperty(target, descriptor.key, descriptor);
      }
   }
   return function (Constructor, protoProps, staticProps) {
      if (protoProps) defineProperties(Constructor.prototype, protoProps);
      if (staticProps) defineProperties(Constructor, staticProps);
      return Constructor;
   };
}();

function _classCallCheck(instance, Constructor) {
   if (!(instance instanceof Constructor)) {
      throw new TypeError("Cannot call a class as a function");
   }
}

var Person = function () {
   function Person(fname, lname, age, address) {
      _classCallCheck(this, Person);

      this.fname = fname;
      this.lname = lname;
      this.age = age;
      this.address = address;
   }

   _createClass(Person, [{
      key: "fullname",
      get: function get() {
         return this.fname + "-" + this.lname;
      }
   }]);

   return Person;
}();

var a = new Person("Siya", "Kapoor", "15", "Mumbai");
var persondet = a.fullname;
JavaScript promises are used to manage asynchronous requests in your code.
It makes life easier and keeps code clean as you manage multiple callbacks from async requests, which have dependency. Promises provide a better way of working with callback functions. Promises are part of ES6. By default, when you create a promise, the state of the promise is pending.
Promises come in three states −
pending (initial state)
resolved (completed successfully)
rejected(failed)
new Promise() is used to construct a promise. Promise constructor has one argument, which is a callback function. The callback function has two arguments - resolve and reject;
both these are internal functions. The asynchronous code which you write, i.e., Ajax call, image loading, timing functions will go in the callback function.
If the task performed in the callback function is a success, then the resolve function is called; otherwise, the reject function is called with the error details.
The following line of code shows a promise structure call −
var _promise = new Promise (function(resolve, reject) {
   var success = true;
   if (success) {
      resolve("success");
   } else {
      reject("failure");
   }
});
_promise.then(function(value) {
   //once function resolve gets called it comes over here with the value passed in resolve
   console.log(value); //success
}).catch(function(value) {
   //once function reject gets called it comes over here with the value passed in reject
   console.log(value); // failure.
});
let timingpromise = new Promise((resolve, reject) => {
   setTimeout(function() {
      resolve("Promise is resolved!");
   }, 1000);
});

timingpromise.then((msg) => {
   console.log(msg);
});
Promise is resolved!

let timingpromise = new Promise((resolve, reject) => {
   setTimeout(function() {
      resolve("Promise is resolved!");
   }, 1000);
});

timingpromise.then((msg) => {
   console.log(msg);
});
npx babel promise.js --out-file promise_es5.js

"use strict";

var timingpromise = new Promise(function (resolve, reject) {
   setTimeout(function () {
      resolve("Promise is resolved!");
   }, 1000);
});

timingpromise.then(function (msg) {
   console.log(msg);
});
For promises, the code is not changing when transpiled. We need to use babel-polyfill for it to work on older browsers.The details on babel-polyfills are explained in babel - poyfill chapter.
Generator function is like normal function. The function has special syntax function* with * to the function and yield keyword to be used inside the function. This is meant to pause or start the function when required. Normal functions cannot be stopped in between once the execution starts. It will either execute the full function or halt when it encounters the return statement. Generator performs differently here, you can halt the function with the yield keyword and start it by calling the generator again whenever required.
function* generatorfunction(a) {
   yield a;
   yield a +1 ;
}

let g = generatorfunction(8);
console.log(g.next());
console.log(g.next());
{value: 8, done: false}
{value: 9, done: false}

function* generatorfunction(a) {
   yield a;
   yield a +1 ;
}

let g = generatorfunction(8);
console.log(g.next());
console.log(g.next());
npx babel generator.js --out-file generator_es5.js

"use strict";

var _marked = /*#__PURE__*/regeneratorRuntime.mark(generatorfunction);

function generatorfunction(a) {
   return regeneratorRuntime.wrap(function generatorfunction$(_context) {
      while (1) {
         switch (_context.prev = _context.next) {
            case 0:
               _context.next = 2;
               return a;

            case 2:
               _context.next = 4;
               return a + 1;
               
            case 4:
            case "end":
               return _context.stop();
         }
      }
   }, _marked, this);
}

var g = generatorfunction(8);
console.log(g.next());
console.log(g.next());
Iterator in JavaScript gives back a JavaScript object, which has value. The object also has a flag called done, which has true/false value. It gives false if it is not the end of the iterator. Let us consider an example and see the working of iterator on an array.
let numbers = [4, 7, 3, 10];
let a = numbers[Symbol.iterator]();
console.log(a.next());
console.log(a.next());
console.log(a.next());
console.log(a.next());
console.log(a.next());
In the above example, we have used an array of numbers and called a function on the array using Symbol.iterator as the index.
The output that we get using the next() on the array is as follows −
{value: 4, done: false}
{value: 7, done: false}
{value: 3, done: false}
{value: 10, done: false}
{value: undefined, done: true}

The output gives an object with value and is done as properties. Every next() method call gives the next value from the array and is done as false. The value of done will be true only when the elements from the array are done. We can use this for iterating over arrays. There are more options available like the for-of loop which is used as follows −
let numbers = [4, 7, 3, 10];
for (let n of numbers) {
   console.log(n);
}
4
7
3
10

When the for-of loop uses the key, it gives details of the array values as shown above. We will check both the combinations and see how babeljs transpiles them to es5.
let numbers = [4, 7, 3, 10];
let a = numbers[Symbol.iterator]();
console.log(a.next());
console.log(a.next());
console.log(a.next());
console.log(a.next());
console.log(a.next());

let _array = [4, 7, 3, 10];
for (let n of _array) {
   console.log(n);
}
npx babel iterator.js --out-file iterator_es5.js

"use strict";

var numbers = [4, 7, 3, 10];
var a = numbers[Symbol.iterator]();
console.log(a.next());
console.log(a.next());
console.log(a.next());
console.log(a.next());
console.log(a.next());

var _array = [4, 7, 3, 10];
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;

try {
   for (var _iterator = _array[Symbol.iterator](),
      _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done);
      _iteratorNormalCompletion = true) {
      var n = _step.value;

      console.log(n);
   }
} catch (err) {
   _didIteratorError = true;
   _iteratorError = err;
} finally {
   try {
      if (!_iteratorNormalCompletion && _iterator.return) {
         _iterator.return();
      }
   } finally {
      if (_didIteratorError) {
         throw _iteratorError;
      }
   }
}

There are changes added for-of loop in es5. But iterator.next is left as it is. We need to use babel-polyfill to make it work in old browsers. Babel-polyfill gets installed along with babel and the same can be used from node_modules as shown below −
<html>
   <head>
      <script type="text/javascript" src="node_modules/babel-polyfill/dist/polyfill.min.js"></script>
      <script type="text/javascript" src="iterator_es5.js"></script>
   </head>
   <body>
      <h1>Iterators</h1>
   </body>
</html>
Destructuring property behaves like a JavaScript expression which unpacks values from arrays, objects.
Following example will explain the working of destructuring syntax.
let x, y, rem;
[x, y] = [10, 20];

console.log(x);
console.log(y);
[x, y, ...rem] = [10, 20, 30, 40, 50];
console.log(rem);

let z = 0;
({ x, y } = (z) ? { x: 10, y: 20 } : { x: 1, y: 2 });
console.log(x);
console.log(y);
10
20
[30, 40, 50]
1
2

The above line of code shows how values are assigned from the right side of the array to the variables on the left side. The variable with ...rem gets all the remaining values from the array.
We can also assign the values from the object on the left side using conditional operator as shown below −
({ x, y } = (z) ? { x: 10, y: 20 } : { x: 1, y: 2 });
console.log(x); // 1
console.log(y); // 2
Let us convert the same to ES5 using babeljs −
npx babel destructm.js --out-file destruct_es5.js

"use strict";

var x = void 0,
   y = void 0,
   rem = void 0;
x = 10;
y = 20;

console.log(x);
console.log(y);
x = 10;
y = 20;
rem = [30, 40, 50];

console.log(rem);

var z = 0;

var _ref = z ? { x: 10, y: 20 } : { x: 1, y: 2 };

x = _ref.x;
y = _ref.y;

console.log(x);
console.log(y);
Template literal is a string literal which allows expressions inside it. It uses backtick(``) instead of single or double quotes. When we say expression inside a string, it means we can use variables, call a function, etc. inside the string.
let a = 5;
let b = 10;
console.log(`Using Template literal : Value is ${a + b}.`);
console.log("Using normal way : Value is " + (a + b));
Using Template literal : Value is 15.
Using normal way : Value is 15

let a = 5;
let b = 10;
console.log(`Using Template literal : Value is ${a + b}.`);
console.log("Using normal way : Value is " + (a + b));
npx babel templateliteral.js --out-file templateliteral_es5.js

"use strict";

var a = 5;
var b = 10;
console.log("Using Template literal : Value is " + (a + b) + ".");

console.log("Using normal way : Value is " + (a + b));
In es6, the new features added to object literals are very good and useful. We will go through few examples of object literal in ES5 and ES6 −
ES5
var red = 1, green = 2, blue = 3;
var rgbes5 = {
   red: red,
   green: green,
   blue: blue
};
console.log(rgbes5); // {red: 1, green: 2, blue: 3}

ES6
let rgbes6 = {
   red,
   green,
   blue
};
console.log(rgbes6); // {red: 1, green: 2, blue: 3}
If you see the above code, the object in ES5 and ES6 differs. In ES6, we do not have to specify the key value if the variable names are same as the key.
Let us see the compilation to ES5 using babel.
const red = 1, green = 2, blue = 3;
let rgbes5 = {
   red: red,
   green: green,
   blue: blue
};
console.log(rgbes5);

let rgbes6 = {
   red,
   green,
   blue
};
console.log(rgbes6);

let brand = "carbrand";
const cars = {
   [brand]: "BMW"
}
console.log(cars.carbrand);  //"BMW"
npx babel enhancedobjliteral.js --out-file enhancedobjliteral_es5.js

"use strict";

function _defineProperty(obj, key, value) {
   if (key in obj) {
      Object.defineProperty(obj, key, {
         value: value, enumerable: true, configurable: true, writable: true
      });
   } else { obj[key] = value; } return obj;
}

var red = 1,
   green = 2,
   blue = 3;
var rgbes5 = {
   red: red,
   green: green,
   blue: blue
};
console.log(rgbes5);

var rgbes6 = {
   red: red,
   green: green,
   blue: blue
};
console.log(rgbes6);

var brand = "carbrand";
var cars = _defineProperty({}, brand, "BMW");

console.log(cars.carbrand); //"BMW"
In this section, we will discuss the default, rest and spread properties.
With ES6, we can use default parameters to the function params as follows −
let add = (a, b = 3) => {
   return a + b;
}

console.log(add(10, 20));  // 30
console.log(add(10));      // 13
Let us transpile the above code to ES5 using babel.
npx babel default.js --out-file default_es5.js

"use strict";

var add = function add(a) {
   var b = arguments.length > 1 >> arguments[1] !== undefined ? arguments[1] : 3;
   return a + b;
};

console.log(add(10, 20));
console.log(add(10));
Rest parameter starts with three dots(...) as shown in the example below −
let add = (...args) => {
   let sum = 0;
   args.forEach(function (n) {
      sum += n;
   });
   return sum;
};

console.log(add(1, 2));    // 3
console.log(add(1, 2, 5, 6, 6, 7));   //27
In the above function we are passing n number of params to the function add. To add all those params if it was in ES5, we have to rely on arguments object to get the details of the arguments. With ES6, rest it helps to define the arguments with three dots as shown above and we can loop through it and get the sum of the numbers.
Note − We cannot use additional arguments when using three dot, i.e., rest.
let add = (...args, value) => {    //syntax error
   let sum = 0;
   args.forEach(function (n) {
      sum += n;
   });
   return sum;
};
The above code will give syntax error.
The compilation to es5 looks as follows −
npx babel rest.js --out-file rest_es5.js

"use strict";

var add = function add() {
   for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
      args[_key] = arguments[_key];
   }

   var sum = 0;
   args.forEach(function (n) {
      sum += n;
   });
   return sum;
};

console.log(add(1, 2));
console.log(add(1, 2, 5, 6, 6, 7));
The Spread property also has the three dots like rest. Following is a working example, which shows how to use the spread property.
let add = (a, b, c) => {
   return a + b + c;
}
let arr = [11, 23, 3];
console.log(add(...arr));   //37
Let us now see how the above code is transpiled using babel −
npx babel spread.js --out-file spread_es5.js

"use strict";

var add = function add(a, b, c) {
   return a + b + c;
};
var arr = [11, 23, 3];
console.log(add.apply(undefined, arr));
Proxy is an object where you can define custom behaviour for operations like property lookup, assignment, enumeration, function, invocation, etc.
var a = new Proxy(target, handler);

Both target and handler are objects.
target is an object or can be another proxy element.
target is an object or can be another proxy element.
handler will be an object with its properties as functions which will give the behaviour when called.
handler will be an object with its properties as functions which will give the behaviour when called.
Let us try to understand these features with the help of an example −
let handler = {
   get: function (target, name) {
      return name in target ? target[name] : "invalid key";
   }
};

let o = {
   name: 'Siya Kapoor',
   addr: 'Mumbai'
}

let a = new Proxy(o, handler);
console.log(a.name);
console.log(a.addr);
console.log(a.age);
We have defined target and handler in the above example and used it with proxy. Proxy returns the object with key-values.
Siya Kapoor
Mumbai
invalid key

Let us now see how to transpile the above code to ES5 using babel −
npx babel proxy.js --out-file proxy_es5.js

'use strict';

var handler = {
   get: function get(target, name) {
      return name in target ? target[name] : "invalid key";
   }
};

var o = {
   name: 'Siya Kapoor',
   addr: 'Mumbai'
};

var a = new Proxy(o, handler);
console.log(a.name);
console.log(a.addr);
console.log(a.age);
 Print
 Add Notes
 Bookmark this page