BabelJS - Transpile ES6 features to ES5


Advertisements

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 + Const

Let declares a block scope local variable in JavaScript. Consider the following example to understand the use of let.

Example

let a = 1;
if (a == 1) {
   let a = 2;
   console.log(a);
}
console.log(a);

Output

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 using ES6

let a = 1;
if (a == 1) {
   let a = 2;
   console.log(a);
}
console.log(a);

Transpiled using babel to ES5

"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.

Const

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.

Example

let a =1;
if (a == 1) {
   const age = 10;
}
console.log(age);

Output

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.

ES6

let a =1;
if (a == 1) {
   const age = 10;
}
console.log(age);

Command

npx babel const.js --out-file const_es5.js

Transpiled to ES6 Using BabelJS

"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.

Arrow Functions

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.

Example

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

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

Output

9

Using BabelJS, we will transpile the above code to ES5.

ES6 - Arrow function

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

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

Command

npx babel arrowfunction.js --out-file arrowfunction_es5.js

BabelJS - ES5

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);

Classes

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.

Example

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;

Output

Siya-Kapoor

ES6 - Classes

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;

Command

npx babel class.js --out-file class_es5.js

BabelJS - ES5

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;

Promises

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.
});

ES6 Promise Example

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

timingpromise.then((msg) => {
   console.log(msg);
});

Output

Promise is resolved!

ES6 - Promises

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

timingpromise.then((msg) => {
   console.log(msg);
});

Command

npx babel promise.js --out-file promise_es5.js

BabelJS - ES5

"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.

Generators

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.

Example

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

let g = generatorfunction(8);
console.log(g.next());
console.log(g.next());

Output

{value: 8, done: false}
{value: 9, done: false}

ES6 - Generator

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

let g = generatorfunction(8);
console.log(g.next());
console.log(g.next());

Command

npx babel generator.js --out-file generator_es5.js

BabelJS - ES5

"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());

Iterators

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.

Example

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 −

Example

let numbers = [4, 7, 3, 10];
for (let n of numbers) {
   console.log(n);
}

Output

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.

Example

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);
}

command

npx babel iterator.js --out-file iterator_es5.js

Output

"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 −

Example

<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>

Output

Babel polyfill

Destructuring

Destructuring property behaves like a JavaScript expression which unpacks values from arrays, objects.

Following example will explain the working of destructuring syntax.

Example

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);

Output

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 −

command

npx babel destructm.js --out-file destruct_es5.js

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 Literals

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.

Example

let a = 5;
let b = 10;
console.log(`Using Template literal : Value is ${a + b}.`);
console.log("Using normal way : Value is " + (a + b));

Output

Using Template literal : Value is 15.
Using normal way : Value is 15

ES6 - Template Literal

let a = 5;
let b = 10;
console.log(`Using Template literal : Value is ${a + b}.`);
console.log("Using normal way : Value is " + (a + b));

command

npx babel templateliteral.js --out-file templateliteral_es5.js

BabelJS - ES5

"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));

Enhanced Object Literals

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 −

Example

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.

ES6-Enhanced object literal

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"

command

npx babel enhancedobjliteral.js --out-file enhancedobjliteral_es5.js

BabelJS - ES5

"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"

Default, Rest & Spread Properties

In this section, we will discuss the default, rest and spread properties.

Default

With ES6, we can use default parameters to the function params as follows −

Example

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.

command

npx babel default.js --out-file default_es5.js

BabelJS - ES5

"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

Rest parameter starts with three dots(...) as shown in the example below −

Example

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.

Example

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 −

command

npx babel rest.js --out-file rest_es5.js

Babel -ES5

"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));

Spread

The Spread property also has the three dots like rest. Following is a working example, which shows how to use the spread property.

Example

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 −

command

npx babel spread.js --out-file spread_es5.js

Babel-ES5

"use strict";

var add = function add(a, b, c) {
   return a + b + c;
};
var arr = [11, 23, 3];
console.log(add.apply(undefined, arr));

Proxies

Proxy is an object where you can define custom behaviour for operations like property lookup, assignment, enumeration, function, invocation, etc.

Syntax

var a = new Proxy(target, handler);

Both target and handler are objects.

  • 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.

Let us try to understand these features with the help of an example −

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.

Output

Siya Kapoor
Mumbai
invalid key

Let us now see how to transpile the above code to ES5 using babel −

command

npx babel proxy.js --out-file proxy_es5.js

Babel-ES5

'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);
Advertisements