• +91 9790 650 659
  • This email address is being protected from spambots. You need JavaScript enabled to view it.

ES6 Features

User Rating: 4 / 5

Star ActiveStar ActiveStar ActiveStar ActiveStar Inactive
 

As the JavaScript community continued to expand rapidly in last few years, a lot of good things have happened to JavaScript as a language. One of those good things was its penetration into the server-side development world via Node.js.

Lets see the ES6 Features available in Javascript.

1. Default Parameters in ES6

Remember we had to do these statements to define default parameters:

var link = function (height, color, url) {
    var height = height || 50
    var color = color || 'red'
    var url = url || 'http://azat.co'
    ...
}

They were okay until the value was 0 and because 0 is falsy in JavaScript it would default to the hard-coded value instead of becoming the value itself. Of course, who needs 0 as a value (#sarcasmfont), so we just ignored this flaw and used the logic OR anyway… No more! In ES6, we can put the default values right in the signature of the functions:

var link = function(height = 50, color = 'red', url = 'http://azat.co') {
  ...
}

By the way, this syntax is similar to Ruby!

2. Template Literals in ES6

Template literals or interpolation in other languages is a way to output variables in the string. So in ES5 we had to break the string like this:

var name = 'Your name is ' + first + ' ' + last + '.'
var url = 'http://localhost:3000/api/messages/' + id

Luckily, in ES6 we can use a new syntax ${NAME} inside of the back-ticked string:

var name = `Your name is ${first} ${last}.`
var url = `http://localhost:3000/api/messages/${id}`

3. Multi-line Strings in ES6

Another yummy syntactic sugar is multi-line string. In ES5, we had to use one of these approaches:

var roadPoem = 'Then took the other, as just as fair,\n\t'
    + 'And having perhaps the better claim\n\t'
    + 'Because it was grassy and wanted wear,\n\t'
    + 'Though as for that the passing there\n\t'
    + 'Had worn them really about the same,\n\t'

var fourAgreements = 'You have the right to be you.\n\
    You can only be you when you do your best.'

While in ES6, simply utilize the backticks:

var roadPoem = `Then took the other, as just as fair,
    And having perhaps the better claim
    Because it was grassy and wanted wear,
    Though as for that the passing there
    Had worn them really about the same,`

var fourAgreements = `You have the right to be you.
    You can only be you when you do your best.`

4. Destructuring Assignment in ES6

Destructuring can be a harder concept to grasp, because there’s some magic going on… let’s say you have simple assignments where keys house and mouse are variables house and mouse:

var data = $('body').data(), // data has properties house and mouse
  house = data.house,
  mouse = data.mouse

Other examples of destructuring assignments (from Node.js):

var jsonMiddleware = require('body-parser').json

var body = req.body, // body has username and password
  username = body.username,
  password = body.password  

In ES6, we can replace the ES5 code above with these statements:

var {house, mouse} = $('body').data() // we'll get house and mouse variables

var {json: jsonMiddleware} = require('body-parser')

var {username, password} = req.body

This also works with arrays. Crazy!

var [col1, col2]  = $('.column'),
  [line1, line2, line3, , line5] = file.split('\n')

It might take some time to get use to the destructuring assignment syntax, but it’s a sweet sugarcoating.

5. Enhanced Object Literals in ES6

What you can do with object literals now is mind blowing! We went from a glorified version of JSON in ES5 to something closely resembling classes in ES6.

Here’s a typical ES5 object literal with some methods and attributes/properties:

var serviceBase = {port: 3000, url: 'azat.co'},
    getAccounts = function(){return [1,2,3]}

var accountServiceES5 = {
  port: serviceBase.port,
  url: serviceBase.url,
  getAccounts: getAccounts,
  toString: function() {
    return JSON.stringify(this.valueOf())
  },
  getUrl: function() {return "http://" + this.url + ':' + this.port},
  valueOf_1_2_3: getAccounts()
}

If we want to be fancy, we can inherit from serviceBase by making it the prototype with the Object.create method:

var accountServiceES5ObjectCreate = Object.create(serviceBase)
var accountServiceES5ObjectCreate = {
  getAccounts: getAccounts,
  toString: function() {
    return JSON.stringify(this.valueOf())
  },
  getUrl: function() {return "http://" + this.url + ':' + this.port},
  valueOf_1_2_3: getAccounts()
}

But for the sake of the example, we’ll consider them similar. So in ES6 object literal, there are shorthands for assignment getAccounts: getAccounts, becomes just getAccounts,. Also, we set the prototype right there in the __proto__`` property which makes sense (notproto’` though:

var serviceBase = {port: 3000, url: 'azat.co'},
    getAccounts = function(){return [1,2,3]}
var accountService = {
    __proto__: serviceBase,
    getAccounts,

Also, we can invoke super and have dynamic keys (valueOf_1_2_3):

    toString() {
     return JSON.stringify((super.valueOf()))
    },
    getUrl() {return "http://" + this.url + ':' + this.port},
    [ 'valueOf_' + getAccounts().join('_') ]: getAccounts()
};

This is a great enhancement to good old object literals!

6. Arrow Functions in ES6

This is probably one feature I waited the most. I love CoffeeScript for its fat arrows. Now we have them in ES6. The fat arrows are amazing because they would make your this behave properly, i.e., this will have the same value as in the context of the function—it won’t mutate. The mutation typically happens each time you create a closure.

Using arrows functions in ES6 allows us to stop using that = this or self = this or _this = this or .bind(this). For example, this code in ES5 is ugly:

var _this = this
$('.btn').click(function(event){
  _this.sendData()
})

This is the ES6 code without _this = this:

$('.btn').click((event) =>{
  this.sendData()
})

Here’s another example in which we use call to pass the context to the logUpperCase() function in ES5:

var logUpperCase = function() {
  var _this = this

  this.string = this.string.toUpperCase()
  return function () {
    return console.log(_this.string)
  }
}

logUpperCase.call({ string: 'es6 rocks' })()

While in ES6, we don’t need to mess around with _this:

var logUpperCase = function() {
  this.string = this.string.toUpperCase()
  return () => console.log(this.string)
}

logUpperCase.call({ string: 'es6 rocks' })()

Note that you can mix and match old function with => in ES6 as you see fit. And when an arrow function is used with one line statement, it becomes an expression, i.e,. it will implicitly return the result of that single statement. If you have more than one line, then you’ll need to use return explicitly.

This ES5 code is creating an array from the messages array:

var ids = ['5632953c4e345e145fdf2df8','563295464e345e145fdf2df9']
var messages = ids.map(function (value) {
  return "ID is " + value // explicit return
})

Will become this in ES6:

var ids = ['5632953c4e345e145fdf2df8','563295464e345e145fdf2df9']
var messages = ids.map(value => `ID is ${value}`) // implicit return

Notice that I used the string templates? Another feature from CoffeeScript… I love them!

The parenthesis () are optional for single params in an arrow function signature. You need them when you use more than one param.

In ES5 the code has function with explicit return:

var ids = ['5632953c4e345e145fdf2df8', '563295464e345e145fdf2df9'];
var messages = ids.map(function (value, index, list) {
  return 'ID of ' + index + ' element is ' + value + ' ' // explicit return
})

And more eloquent version of the code in ES6 with parenthesis around params and implicit return:

var ids = ['5632953c4e345e145fdf2df8','563295464e345e145fdf2df9']
var messages = ids.map((value, index, list) => `ID of ${index} element is ${value} `) // implicit return

7. Promises in ES6

Promises have been a controversial topic. There were a lot of promise implementations with slightly different syntax. q, bluebird, deferred.js, vow, avow, jquery deferred to name just a few. Others said we don’t need promises and can just use async, generators, callbacks, etc. Gladly, there’s a standard Promise implementation in ES6 now!

Let’s consider a rather trivial example of a delayed asynchronous execution with setTimeout():

setTimeout(function(){
  console.log('Yay!')
}, 1000)

We can re-write the code in ES6 with Promise:

var wait1000 =  new Promise(function(resolve, reject) {
  setTimeout(resolve, 1000)
}).then(function() {
  console.log('Yay!')
})

Or with ES6 arrow functions:

var wait1000 =  new Promise((resolve, reject)=> {
  setTimeout(resolve, 1000)
}).then(()=> {
  console.log('Yay!')
})

So far, we’ve increased the number of lines of code from three to five without any obvious benefit. That’s right. The benefit will come if we have more nested logic inside of the setTimeout() callback:

setTimeout(function(){
  console.log('Yay!')
  setTimeout(function(){
    console.log('Wheeyee!')
  }, 1000)
}, 1000)

Can be re-written with ES6 promises:

var wait1000 =  ()=> new Promise((resolve, reject)=> {setTimeout(resolve, 1000)})

wait1000()
  .then(function() {
    console.log('Yay!')
    return wait1000()
  })
  .then(function() {
    console.log('Wheeyee!')
  })

Still not convinced that Promises are better than regular callbacks? Me neither. I think once you got the idea of callbacks and wrap your head around them, then there’s no need for additional complexity of promises.

8. Block-Scoped Constructs Let and Const

You might have already seen the weird sounding let in ES6 code. I remember the first time I was in London, I was confused by all those TO LET signs. The ES6 let has nothing to do with renting. This is not a sugarcoating feature. It’s more intricate. let is a new var which allows to scope the variable to the blocks. We define blocks by the curly braces. In ES5, the blocks did NOTHING to the vars:

function calculateTotalAmount (vip) {
  var amount = 0
  if (vip) {
    var amount = 1
  }
  { // more crazy blocks!
    var amount = 100
    {
      var amount = 1000
      }
  }  
  return amount
}

console.log(calculateTotalAmount(true))

The result will be 1000. Wow! That’s a really bad bug. In ES6, we use letto restrict the scope to the blocks. Vars are function scoped.

function calculateTotalAmount (vip) {
  var amount = 0 // probably should also be let, but you can mix var and let
  if (vip) {
    let amount = 1 // first amount is still 0
  } 
  { // more crazy blocks!
    let amount = 100 // first amount is still 0
    {
      let amount = 1000 // first amount is still 0
      }
  }  
  return amount
}

console.log(calculateTotalAmount(true))

The value is 0, because the if block also has let. If it had nothing (amount=1), then the expression would have been 1.

When it comes to const, things are easier; it’s just an immutable, and it’s also block-scoped like let. Just to demonstrate, here are a bunch of constants and they all are okay because they belong to different blocks:

function calculateTotalAmount (vip) {
  const amount = 0  
  if (vip) {
    const amount = 1 
  } 
  { // more crazy blocks!
    const amount = 100 
    {
      const amount = 1000
      }
  }  
  return amount
}

console.log(calculateTotalAmount(true))

In my humble opinion, let and const overcomplicate the language. Without them we had only one behavior, now there are multiple scenarios to consider. ;-(

9. Classes in ES6

If you love object-oriented programming (OOP), then you’ll love this feature. It makes writing classes and inheriting from them as easy as liking a comment on Facebook.

Classes creation and usage in ES5 was a pain in the rear, because there wasn’t a keyword class (it was reserved but did nothing). In addition to that, lots of inheritance patterns like pseudo classical, classical, functional just added to the confusion, pouring gasoline on the fire of religious JavaScript wars.

I won’t show you how to write a class (yes, yes, there are classes, objects inherit from objects) in ES5, because there are many flavors. Let’s take a look at the ES6 example right away. I can tell you that the ES6 class will use prototypes, not the function factory approach. We have a class baseModel in which we can define a constructor and a getName() method:

class baseModel {
  constructor(options = {}, data = []) { // class constructor
    this.name = 'Base'
    this.url = 'http://azat.co/api'
    this.data = data
    this.options = options
  }

    getName() { // class method
      console.log(`Class name: ${this.name}`)
    }
}

Notice that I’m using default parameter values for options and data. Also, method names don’t need to have the word function or the colon (:) anymore. The other big difference is that you can’t assign properties this.NAME the same way as methods, i.e., you can’t say name at the same indentation level as a method. To set the value of a property, simply assign a value in the constructor.

The AccountModel inherits from baseModel with class NAME extends PARENT_NAME:

class AccountModel extends baseModel {
  constructor(options, data) {

To call the parent constructor, effortlessly invoke super() with params:

    super({private: true}, ['32113123123', '524214691']) //call the parent method with super
     this.name = 'Account Model'
     this.url +='/accounts/'
   }

If you want to be really fancy, you can set up a getter like this and accountsData will be a property:

 get accountsData() { //calculated attribute getter
    // ... make XHR
    return this.data
  }
}

So how do you actually use this abracadabra? It’s as easy as tricking a three-year old into thinking Santa Claus is real:

let accounts = new AccountModel(5)
accounts.getName()
console.log('Data is %s', accounts.accountsData)

In case you’re wondering, the output is:

Class name: Account Model
Data is %s 32113123123,524214691

10. Modules in ES6

As you might now, there were no native modules support in JavaScript before ES6. People came up with AMD, RequireJS, CommonJS and other workarounds. Now there are modules with import and export operands.

In ES5 you would use <script> tags with IIFE, or some library like AMD, while in ES6 you can expose your class with export. I am a Node.js guy, so I’ll use CommonJS which is also a Node.js syntax. It’s straightforward to use CommonJS on the browser with the Browserifybunder. Let’s say we have port variable and getAccounts method in ES5 module.js:

module.exports = {
  port: 3000,
  getAccounts: function() {
    ...
  }
}

In ES5 main.js, we would require('module') that dependency:

var service = require('module.js')
console.log(service.port) // 3000

In ES6, we would use export and import. For example, this is our library in the ES6 module.js file:

export var port = 3000
export function getAccounts(url) {
  ...
}

In the importer ES6 file main.js, we use import {name} from 'my-module'syntax. For example,

import {port, getAccounts} from 'module'
console.log(port) // 3000

Or we can import everything as a variable service in main.js:

import * as service from 'module'
console.log(service.port) // 3000

The support for ES6 modules in the browsers are not coming anytime soon (as of this writing), so you’ll need something like jspm to use ES6 modules.

ES6 is finalized, but not fully supported by all browsers. To use ES6 today, get a compiler like Babel. You can run it as a standalone tool or use with your build system. There are Babel plugins for Grunt, Gulp and Webpack.

11.  find/findIndex

JavaScript gives developers Array.prototype.indexOf to get the index of a given item within an array, but indexOf doesn't provide a method to calculate the desired item condition; you also need to search for an exact known value.  Enter find and findIndex -- two methods for searching an array for the first match of a calculated value:

let ages = [12, 19, 6, 4];

let firstAdult = ages.find(age => age >= 18); // 19
let firstAdultIndex = ages.findIndex(age => age >= 18); // 1

find and findIndex, through allowing a calculated value search, also prevent unnecessary side effects and looping through possible values!

12.  The Spread Operator: ... 

The spread operator signals that an array or iterable object should have its contents split into separate arguments within a call.  A few examples:

// Pass to function that expects separate multiple arguments
// Much like Function.prototype.apply() does
let numbers = [9, 4, 7, 1];
Math.min(...numbers); // 1

// Convert NodeList to Array
let divsArray = [...document.querySelectorAll('div')];

// Convert Arguments to Array
let argsArray = [...arguments];

The awesome added bonus is being able to convert iterable objects (NodeList, arguments, etc.) to true arrays -- something we've used Array.from or other hacks to do for a long time.