Looking forward to the future of JavaScript

What we're excited about in ES6 and beyond

The future of Javascript
Here at GoSquared, we’re massive fans of JavaScript. Pretty much everything we code is written in JavaScript, be it on the backend (using Node.js) or on the frontend.

JavaScript is a changing language, though, and there’s some particularly exciting new features in the process of being specified for ECMAScript (the official specification behind JS) versions 6 and beyond.

ES6 isn’t due to reach final specification status for a while yet, but some browsers have already started implementing some of the new features. ES7 is still way off in the future, but there are some interesting new features planned.

How to use these features now

Browser support for ES6 features is somewhat patchy at best at the moment, but it’s possible to write using ES6 and 7 and “transpile” it using Google’s Traceur Compiler to the JavaScript-of-today that’s understood by browser and Node.js. Traceur can be used as a drop-in replacement for require in Node, or you can compile to browser-compatible javascript, either directly or using tools like grunt or gulp.

Our favourite new JavaScript features

There are a lot of features available in ES6 and through Traceur, but here are a few of our faves, with some real(ish)-world examples.

Arrow Functions

Arrow functions replace function(args){ return expression; } with (args) => expression or function(args){ /* do something */ } with (args) => { /* do something */ }. Additionally they also use lexical this scoping (i.e. they inherit the this from the scope in which they’re written, rather than depending on where and how they’re called), which is particularly useful if you want to avoid using bind all over the place in event handlers and callbacks.

They are particularly useful in the argument => returnExpression form, especially if you’re using libraries like D3 which use a lot of small functions. For example:

someCircles
  .data(data, d => d.id)
  .sort((a, b) => a.score - b.score)
  .attr('transform', d => `translate(${d.x}, ${d.y})`)
  .attr('r', d => d.radius);

Much tidier than having function and return all over the place. The above example also uses a template literal which is a neat way of interpolating values into strings without lots of quote marks and concatenation.

Rest and Spread

Rest and spread are like inverses of each other. Rest parameters allow you to use function(arg1, arg2, ...allTheRest){ ... }, and all arguments after the first two will be placed as elements of allTheRest (which is actually a proper array, rather than a confusing arguments object). The spread operator allows you to expand an array to formal parameters, so you can call a function like someFunction(...arrayOfParameters).

Here’s an example of using rest parameters to implement something similar to jQuery’s $.merge:

function merge(into, ...others){
  var src;
  while (src = others.shift()) {
    Object.keys(src).forEach(k => into[k] = src[k]);
  }
  return into;
}

Spread is particularly useful also for doing things like Math.max(...bigArrayOfNumbers), or for example on the backend with Express using app.get('/', ...middlewareChain, routeHandler)

Object literal shorthand and computed property names

Object literals have been improved to allow you to write, for example, { x, y } if you already have variables called x and y (instead of { x: x, y: y }).

Computed property names allow you to specify properties in object literals based on expressions, one place we’ve found that particularly useful for front-end development is in handling CSS with browser prefixes:

/* var width, height, x, y = foo; */

// use a shared variable to store browser-prefixed version
// of the transform property (e.g "WebkitTransform" or "transform")
var cssTransformProperty = getPrefixed('Transform');

$('<div>').css({
  width, height,

  [cssTransformProperty]: `translate(${x}px, ${y}px)`
});

// This is basically equivalent to:
var obj = { width, height };
obj[cssTransformProperty] = `translate(${x}px, ${y}px)`;
$('<div>').css(obj);

Destructuring assignment

Destructuring assignment is a way of assigning several variables at once.

You can destructure arrays:

var [ rootItem, subItem, ...allTheRest ] = url.split('/');

// equivalent to:
var arr = url.split('/');
var rootItem = arr[0], subItem = arr[1], allTheRest = arr.slice(2);

You can destructure objects (including shorthand):

// utils = { doThis: function(){}, doThat: function(){}, ... }

var { doThis, doThat, etc } = utils;

// now use doThis()

You can also destructure inside function arguments:

var arrayOfPeople = [ { name: 'bob', email: 'bob@company.com' }, ... ];

$('<ul>').html(
  arrayOfPeople
    .map(({ name, email }) => `<li>${name} (${email})</li>`)
    .join('')
);

Block Binding (let and const)

Let’s face it, we’ve all done something like this before:

for(var i = 0; i < arr.length; i += 1){
  setTimeout(function(){
    // something with arr[i], which always gets executed with i = arr.length
  }, 100);
}

Obviously there are workarounds, most of which involve messy immediately-invoked functions. ES6 introduces two new keywords, let and const which are scoped to their local block rather than just function scope that var uses. Now you can do something like this:

for(let i = 0; i < arr.length; i += 1){
  setTimeout(function(){
    // something with arr[i], which works!
  }, 100);
}

Async Functions

Async functions are still very experimental. They’re lined up for inclusion in ES7, but there’s plenty of scope for them to change before being finalised.

NB: the examples below are correct at the time of writing, but syntax may well change in the future

Async functions uses Promises to allow you to write asynchronous code without having to deal with callbacks or Promises directly.

A function written with async function(){ ... } will return a Promise that either resolves to the return value of the function’s body, or rejects if the function throws an error.

There’s a new keyword, await, which can only be used inside an async function, which when given a promise, will evaluate (asynchronously) to the resolved value of the Promise.

For example:

function timeout(ms){
  return new Promise(resolve => setTimeout(resolve, ms));
}

async function asyncValue(value){
  await timeout(500);
  return value;
}

(async function(){
  var value = await asyncValue(42);
  // half a second later, value now has the value 42

  doSomethingElse();
})();

Async functions are particularly useful on the backend, where you may be fetching data asynchronously.

function getConfig(id){
  return new Promise(() => {
    // imagine fetchSomeData is some nodey-style err-first-callback function
    fetchSomeData(id, (err, data) => err ? reject(err) : resolve(data));
  });
}

expressApp.get('/', async function(req, res, next){
  var conf = await getConfig(req.session.id);
  return res.render('index', conf);
});

// You can also use Promise.all or Promise.race to await multiple promises

expressApp.get('/list', async function(req, res, next){
  var configs = await Promise.all([ 1, 2, 3 ].map(getConfig));
  res.render('list', { configs });
});

It’s probably not advisable, however, to use async functions in frontend code: Traceur compiles them to fairly complex state machines, which can get very large very quickly. Here’s that first example compiled just to show how big it can get.

But wait, there’s more!

This is far from an exhaustive list. I’ve merely touched on the features that we use most here at GoSquared. There’s plenty more, including classes, modules, generators, array comprehension, for-of loops, Maps, Sets, Proxies, and way more that I haven’t mentioned. This page documents all the features currently supported by Traceur, and here’s a quick one-page rundown of everything that’s proposed.

Obviously these features may change before the stable release so don’t take anything written here as certain. I would love to hear about your favourite features in the comments or discuss anything about specific implementation.

Never miss a post