Getting started with ES6

Peter Weber

The current javascript landscape is becoming famous for the chaos and uncertainty of new libraries, tools, pre-compilers, compilers, transpilers, task runners and techniques.

If you read about developing javascript now the common theme is the overwhelming array of options, and the frustration with keeping up and using the best and latest.

There are competing tools, new trends to follow and even new languages which compile into javascript. For novice and experienced front-end developers alike settling on a workflow is daunting and confusing.

The javascript language itself is changing too, with new features to be added regularly for syntax and feature improvements. While it may be tempting to continue writing jQuery flavor javascript and wait for the dust to settle, here’s a few reasons why you should get started with modern js and some tools that can help.

Native improvements

ES6 (also known as ES2015) is an update of the javascript language which includes many new improvements, but it’s important to remember that the javascript community has been “hacking” the language for years, writing libraries and utilities to add useful features to the language. Prototype, jQuery, MooTools- then later Underscore, Lodash and a galaxy of others have all helped to extend javascript’s capabilities. ES6 has incorporated many of these extensions as native features, meaning by writing modern javascript you may reduce your dependency on extra utility libraries.

Variable declarations

With ES6 are 2 new ways of declaring a variable- const and let.

These additions help you with planning and can prevent errors, giving helpful warnings in your console if you misuse a variable. Setting a const means that value can’t be changed later, while let is meant for changeable or throwaway variables such as an incrementer in a loop or a value changed by a logical statement.

Unlike the function scoped var, const and let are scoped by block which includes loops and if statements. In a loop or if statement each would have its own scope for let. While redeclaring a var will override a value in it's outer scope, let keeps variables from “leaking” and prevents accidental changes.

const truthy = true;
 
var bike = 'track';
let rider = 'messenger';
 
if (truthy) {
  var bike = 'road';
  let rider = 'commuter';
}
 
// outputs 'road', overriding previous value
console.log(bike);
 
// outputs 'messenger' since 2nd "let" is inside its own block scope
console.log(rider);
 

Arrow Functions

Besides looking cleaner and eliminating some cruft from your anonymous functions, arrow functions have a major advantage- scope. Arrow functions do not bind their own "this", which can be confusing and requires extra code to work around.

In this contrived example, creating an instance of the Example class and calling the method doLog, this.logThis() refers to a method on the same object. However, in the function called by the setTimeout, this has been rebound to window or the global object, and you'll get an error that logThis is not a function.

class Example {
 
  logThis() {
    console.log('just logged');
  }
 
  doLog() {
    this.logThis();
 
    setTimeout(function() {
      this.logThis();
    }, 500);
  }
}
 
const ex = new Example();
 
// outputs 'just logged'
// outputs 'Uncaught TypeError: this.logThis is not a function'
ex.doLog();

One way around this would be to create a new variable just to access this within a new scope.

doLog() {
  var self = this;
 
  setTimeout(function() {
    self.logThis();
  }, 500);
}

It works, but it's clumsy and verbose. With an arrow function, this is not re-bound and you may call the function without any tricks.

class Example {
 
  logThis() {
    console.log('just logged');
  }
 
  doLog() {
    this.logThis();
 
    setTimeout(() => {
      this.logThis();
    }, 500);
  }
}
 
const ex = new Example();
 
// outputs 'just logged'
// outputs 'just logged' again after 500ms
ex.doLog();

Module loading

Breaking your code into smaller, discrete modules makes it more readable, reusable and friendlier to your team.

There have been a number of ways to do this in the past, but with ES6 there is finally a standard import syntax.

Unfortunately there is not good browser support for "import" yet, but it's a good idea to start using it now, since ES6 modules will be native to browsers in the future and can currently be implemented by Babel or Rollup or the packer of your choice.

Functions, classes and variables can all be exported from one module and “imported” to another, even allowing you to selectively use single functions from larger files.

Build Tools

Most ES6 features are available in modern browsers, but besides being able to take advantage of less supported features like "import" it's still a good idea to make your javascript more compatible with IE and older browsers.

The intimidating part of using the new syntactic sugar from ES6 is setting up a build script for transpiling your new code into something current browsers can all interpret.

Babel is the standard for transpiling modern js into ES5, the previous and widely supported version of javascript. It’s easy to get lost at this step- browserify or webpack? Grunt or gulp? SystemJS? Rollup?

This is an area where many developers get stuck, as there's many options and it's not always clear which to choose from. Build tools should be fairly interchangeable so if you do want to replace it later it shouldn't affect your app. The key is to pick one, use it, then change part of your tool chain only when necessary. There’s no need to stress over the variety of choices and the various strengths and weaknesses- just grab a script that works and start writing code!

To decide where to start, begin with Babel then choose a bundler like Browserify and a task runner like gulp. With this in mind, search for a gulpfile that covers the features you need.

Here's a gist which uses Babel and Browserify, as well as Watchify. With Watchify, any changes to any of my “imported” files trigger a new build. Watchify is very fast and will notice changes to any of the files you're importing into your main file.

Transpiling means the output javascript is vastly different from the code you wrote, much more verbose and often using language constructs you might not be familiar with, as well as new variables to account for scope in your arrow functions or to replace variables declared with const or let. Using Chrome’s source tab in the dev tools pane sends you into this unfamiliar file and makes debugging difficult. The solution to this is “sourcemaps” which keep track of the source lines of code in your original files and map them to the compiled javascript. The gist above includes a step for generating a sourcemap file which Chrome automatically loads with dev tools making it much more straightforward to debug and step through your code.

It’s easy to get overwhelmed with the options available, and the competing tools which serve similar purposes, but it’s important to remember that all this activity is a sign of a healthy, vibrant community, all trying to solve the same problems. Learning ES6 now doesn’t have to be a huge undertaking, try out a few nice features and then slowly incorporate more into your repertoire.