ES2015(ES6) & Babel: Why I don't miss CoffeeScript anymore!

Many people hate CoffeeScript, and it includes many prominent developers. For them, CoffeeScript doesn’t solve any problems and adds a layer of complexity. Also, the new syntax and getting used to the significance of whitespace has some learning curve. On the other hand, it added much-needed functionality to JavaScript: classes, inheritance, ‘fat arrow’ and so on. At this point, if you are already flinching at the mention of the classes and inheritance, I understand your sentiments. I used CoffeeScript a lot since 2011 on projects with reasonably large size and complexity. Here are the reasons I decided to go with CoffeeScript in 2011:

  • It added missing functionality to JavaScript. Of course, it was possible to do same things in plain JavaScript(after all it compiles to JavaScript), but it was somewhat cumbersome.
  • It gave a ‘standard’ way of doing things which could be done in plain JavaScript using half a dozen equally valid ways, removing the burden of decision making and bringing consistency.
  • It requires much less typing and decorations of curly brackets, semi-colons, etc. which made developers productive
  • It allowed to write succinct code, clean and easy to read.
  • For developers coming from the object-oriented background with limited JavaScript exposure: classes, inheritance, etc. made them feel right at home.

Fast forward to the year 2015: lots of cool things are happening with the JavaScript. ES6 features(now referred as ES2015) is becoming widely available in the browsers. Transpilers and shims are becoming mature and widely adopted.

At this point, around ~65% of the ES2015 specs is available in the latest version of Firefox and Chrome, you could see the detailed compatibility chart here. It will take some time before all the browsers catch up, however transpilers like Babel, Traceur are helping to bridge this gap. Transpilers are source-to-source compilers which converts ES2015 features to widely supported ES5 specs. Great communities are forming around the transpilers, and it is being used in production by many wellknown players. I am currently using Babel, so far my experience has been very pleasant, and it seamlessly fits in the development workflow. Here are few of my ES2015 favorite features which makes me not miss the CoffeeScript!

1) Arrow functions

Eliminates the need for managing this scope using .bind or self = this. It doesn’t change the way your code in a big way, but it’s a nice convenience. Also, it’s less verbose which comes in handy while defining anonymous functions.

function refreshView(){

}

$("#refreshButton").bind('click', (e)=> { 
  this.refreshView();
});

2) Classes

Yes, now classes are natively supported in ES2015

class Computer {
  constructor(make){
  	this.make = make;
  }
  
  boot(){
    console.log("Booting...");
  }
  
}

class SuperComputer extends Computer {
  constructor(make, architecture, flops) {
    super(make);
    this.architecture = architecture;
    this.flops = flops;    
  }
  
  doSuperCompute() {
    console.log("Doing super computing");
  }
}

3) Modules

Module is one of the most important feature in any language for organizing the code and managing the scope. Modules were missing from the JavaScript, however CommonJS and AMD (Asynchronous Module Definition) has been around for quite some time. Now, modules are natively supported with ES2015, so no excuse for not using it!

//util.js
export function validateEmail(value){
}

export function validateIP(value){
}

export const emptyGuid = '00000000-0000-0000-0000-000000000000'

It could be imported using

import { validateEmail, validateIP } from 'util'

or the complete module

import * as util from 'util'

exports would be available as properties

util.validateEmail('xxxx')
util.validateIP('xxxx')
util.emptyGuid

It also supports default export, which could be imported without specifying any name. Only one default export per file is supported.

//helper.js
default export createUUID(){
}

default exports could be imported as

import UUIDGenerator form 'helper'

Class could be exported as

//or class export - graphhelper.js
default export class GraphHelper{

}

which could be used as

import GraphHelper from 'graphhelper'

4) Promises

Promises allow to write elegant code around asynchronous operations. Agree, you could achieve same results using the callbacks and events. However, promises makes the code easy to read and introduces standard pattern to follow. ES2015 has adopted the Promises/A+ specs which is widely adopted by the community.

Creating a promise is very straight forward

function findMatch(value){
  return new Promise((resolve, reject)=> {
    //perform asynchronous operation here...
    if(success){
      resolve(result);
    }
    else{
      reject(error);
    }
  })
 }

It could be used as

findMatch('xxxx').then(result => console.log(result))
.catch(error => console.log("Error occurred."));

5) New Object Literal Features

These are an excellent addition to the way you declare objects.

//property value shorthand
var product='SSD', category='computer', specs={ size: '500gb',speed:'650mbps' }
var ssd = {product, category, specs}

//computed property
var customAttrib = "warranty";
var monitor = {
  [customAttrib]: "5 years"
}

//method declaration shorthand
var helper = {
  render(){
    
  },
  
  rest(){
    
  }
};

ES7 & Experimental Features

Babel also has experimental support for ES7 features - see details here. A word of caution - these feature might take really long before it is implemented by the browsers, it might change or even not make it to the final specs. These features are tagged by stages based on where it stands.

  • Stage 0 - Strawman
  • Stage 1 - Proposal
  • Stage 2 - Draft
  • Stage 3 - Candidate
  • Stage 4 - Finished

Features at Stage 2 and above are automatically enabled, for others you will have to pass an argument, for example

babel --stage 0