A Quick Tour Of ES6 (Or, The Bits You’ll Actually Use)

Just want examples of ES6 in use? I’ve put four ES6 projects on Github.

ES6 has been getting a lot of press lately. And with all of the benefits that the new syntax and built-ins bring, it damn well should. But given the breadth of the changes, how is a forward-thinking developer to know where to start? What is worth practicing now, and what is best left for a rainy day?

Well, after spending the last few weeks writing maxim and memamug with ES6, and translating numbat-ui from coffeescript, there are a few parts which have stood out to me.

Destructuring

They say the simpler things in life are sometimes the best, and so it goes with ES6. In fact, the feature which has left the biggest impression on me isn’t Promises, isn’t Generators, and isn’t Classes – it is Destructuring.

Why? Have an example:

ES6

function initialize({controls = {}, models = {}, reducers = {}, actors = []}) {
    // ...
}

ES5

function initialize(options) {
    var controls = options.controls || {};
    var models = options.models || {};
    var reducers = options.reducers || {};
    var actors = options.actors || {};
}

ES6 code taken from index.js in Maxim

As seen here, destructuring works wonders for function arguments. But it also works great with:

for .. of loops

Say you’ve got an Array of pairs as you’d get from the Object.entries method proposed for ES7, and you want to loop through them. for .. of with destructuring makes this (almost) fun:

ES6

for (let [name, builder] of Object.entries(models)) {
    // do something
}

ES5

var entries = Object.entries(models);
for (var i = 0; i != entries.length; i++) {
    var entry = entries[i];
    var name = entry[0];
    var builder = entry[1];
}

ES6 code taken from index.js in Maxim

This is nice, but why limit ourselves to iterating through arrays? The beauty of ES6 is that Arrays aren’t special anymore – you can use for .. of to iterate through anything which defines an Iterator. Like:

Map

Maps are kind of like objects – they map keys to values. The two major differences are:

  • Object can only have Strings and Symbols as keys (Maps can have almost anything)
  • Maps are iterable

In fact, a Map’s iterator produces 2-element arrays each with a key/value pair – like the above example. This allows you to do neat things like this:

ES6

this.propFns = this.propFns || new Map

for (const event of events) {
  const name = 'on' + capitalize(event)
  if (!this.propFns.has(name)) {
    this.propFns.set(name, [])
  }
  this.propFns.get(name).push(fn)
}

// later...

for (let [name, fns] of this.propFns || []) {
  // ...
}

ES5

this.propFns = this.propFns || {}

events.forEach(function(event) {
    const name = 'on' + capitalize(event)
    if (!this.propFns[name]) {
        this.propFns[name] = []
    }
    this.propFns[name].push(fn)
})

// later...

for (var name in this.propFns) {
    if (this.propFns.hasOwnProperty(name)) {
        var fns = this.propFns[name];
        // ..
    }
}

Code inspired by base.js in react-base

While there isn’t too much difference in setting the two examples up, actually using a Map of keys/pairs feels much more elegant than the equivalent example using Objects. Map also has Map.prototype.values and Map.prototype.keys available, in case you don’t need them both keys and values.

Have you noticed that I’ve been using some new syntax without introducing it? Drum roll please. Let’s do so now!

let / const

While everything I’ve shown you until now makes JavaScript feel noticeably nicer, let makes it feel a lot less icky.

Why? Code speaks louder than words:

ES5

// Find the index of the first ripple which has started but isn't yet ending
for (var i = 0; i != ripples.length; i++) {
    if (ripples[i].started && !ripples[i].ending) {
        break;
    }
}

// Do something unrelated which we probably added at a later date
for (var j = 0; j != arr.length; j++) {
    for (var i = 0; i != arr[j].length; i++) {
        // do something...
    }
}

// Oops.
var endingRipple = ripples[i];

Example inspired by TouchRipple.jsx in numbat-ui

So the above code doesn’t work. I’m sure you know what the problem is, but have a think about it anyway. Once you’ve got it, check by touching or clicking the box below:


The i variables which holds the result of our first loop is modified in the second loop, before we try and use it to access the ripple object we were looking for.

Why? Variables defined with var aren’t actually defined where the var appears – they’re defined at the top of the function they appear in (yes – the function – not the loop).

You may think you won’t ever make this mistake. But if you’re using ES5, you will.

However, using let in place of var in the above code would have actually produced the result we expect, as variables defined with let only come into existence in the block of code they’re defined in. I.e. they’re block-scoped variables.


const is just like let, except you can’t re-assign it. Be careful though, you can still modify any objects/arrays which it points to:
const lyrics = 'badger';
lyrics = 'mushroom';
// ERROR!

const lyrics = ['badger', 'badger', 'badger', 'badger', 'badger', 'badger', 'badger', 'badger', 'badger', 'badger', 'badger', 'badger'];
lyrics.push('mushroom').
// OK

Of course we’d never actually write any code like the above example in ES6, because of the new:

ES6 Array methods

In particular, Array.prototype.findIndex will give you the index of the first item from an array which matches the given predicate function.

This means we can rewrite the first loop in the above example like this:

const i = ripples.findIndex(function(r) { return r.started && !r.ending; });

But why return the index when you can just return the object you’re looking for straight up with Array.prototype.find?

const endingRipple = ripples.find(function(r) { return r.started && !r.ending; });

Other new array methods include:

Of course, it is all well and good knowing these methods exist – but unless you’re a memorisation pro, it’ll be a while before you can use them without googling. That is, unless you have my print-optimised ES6 cheatsheet! It describes each of the new array methods, and you’ll receive it immediately after subscribing to my newsletter. Convenient! But I digress.

Get the PDF cheatsheet!

Arrow Functions

See that one-liner function in the example above? That was a contrived example. In the real ES6 code I took the example from, instead of using the old function syntax to define a function, I instead used the new => syntax.

const endingRipple = ripples.find(r => r.started && !r.ending);

Code is from TouchRipple.jsx in numbat-ui

Very pretty, right? Actually, very normal if you’ve been writing coffeescript for the past few years, but I digress.

Arrow functions can take on a number of forms. Here is a decidedly less pretty one:

ES6

const targetFactory = ({options, children}) => {
  let zIndex = 1
  if (this.props.disabled) zIndex = 0
  else if (this.state.touched) zIndex = 2

  const inner = this.props.targetFactory(
    Object.assign(options, {className: this.c('container')}),
    React.createElement(Paper, {zDepth}, children)
  )

  return React.createElement(Target, {on: this.setTouched, off: this.unsetTouched}, inner)
}

Code from RaisedButton.jsx in numbat-ui

See the this inside the function? In a normal function, you’d have no fucking clue what this this meant if you passed the function to someone else, unless you bound it with Function.prototype.bind. With =>, this means the same thing inside the function as it does outside. Even if you pass it somewhere else. Even if you bind it to something else.

In short, function(args) { return ... }.bind(this) is equivalent to args => ...

Except when it’s not:

  • You need to use braces around the function contents if you use more than a single expression
  • If you use braces, you still need a return statement
  • You can’t emit the brackets around a single function argument if you use object destructuring on it

All these rules can be a little hard to remember. But don’t worry! It’s all in the cheatsheet at the bottom of the post. So let’s move on to:

Template Literals

Template literals (i.e. strings delimited by `` backticks) let you:

  • Interpolate JavaScript into your string using ${}
  • Write multiple lines of string
  • Do all sorts of other crazy things like “tagging” them

Honestly, I just use the interpolation. That’s pretty boring, so let’s throw ES7’s proposed fetch method into the example to spice it up a little:

const promise = fetch('/api/v1/contacts', {
    method: 'post',
    headers: {
        'Authorization': `Token token=${identity.get('accessToken')}`,
        'Accept': 'application/json',
        'Content-Type': 'application/json'
    },
    body: JSON.stringify(body)
})

Taken from ContactControl.js in memamug-client

See that promise variable we assigned the result of fetch to? It’s value is a built-in class new to ES6. It’s name, surprise surprise, is:

Promise

Promises are a way of making asynchronous code nicer. In the example above, the promise represents the result of the HTTP fetch. We can add callbacks to promises to be notified when the request completes (or fails valiantly) using their then(onSuccess, onFailure) method.

Promises aren’t a small topic, so a full explanation wouldn’t fit here. But heres one I prepared earlier:

Introduction to ES6 Promises – The Four Functions You Need To Avoid Callback Hell

Now, moving right along to:

… (spread operator and rest parameters)

Known as splats in some other languages, these provide a convenient way to shove any unnamed arguments passed to your function into an array, or do the inverse by calling a function with arguments extracted from an array.

Again, you’ll probably find this easier to understand by looking at it:

c(...args) {
  return (
    classNames(...args)
      .split(/\s+/)
      .filter(name => name !== "")
      .map(name => `{namespace}-${this.constructor.name}-${name}`)
      .join(' ')
  );
}

Taken from base.js in react-base

So you can pass any number of arguments to c, and they’ll end up in the args array. Similarly, classNames will then receive these as separate arguments, not as a single array.

On a completely different note, see how we’re defining this function without the function keyword? This trick is actually part of the syntax for:

Classes

Yep, ES6 has classes. These could have an entire article written about them just by themselves. Maybe I’ll write one, one day. You’ll find out if you sign up to my newsletter. Just like you’ll find out when I write more about:

import & export

Actually, import and export are probably my favourite ES6 feature, but I’ll leave these for a later post. You won’t be using them directly in browsers anytime soon, but combined with a good build setup, you don’t have to use them directly in browsers. But why wait until I write about class, import and export, when almost every file I’ve pulled examples from so far uses them?

Example ES6 code

It is all available at my github, with four projects I’d recommend:

Need a little help getting an environment where you can use ES6 up and running? Check out my article on getting started with webpack.

Cheatsheet

Phew, that was a lot of information. The key to remembering it all is to use it, but searching through this article for just the bit you want each time sure sounds like a lot of work.

Thats why I’ve created this spiffy print-optimised cheat sheet (see preview). It is perfect for printing out and hanging next to your monitor or on your toilet door, or to use in fashionable paper-mache clothing.

The high resolution PDF is available exclusively to my newsletter subscribers – sign up now and it’ll be immediately e-mailed to you for your reading pleasure, along with a handy-dandy JavaScript Promises cheatsheet!

I will send you useful articles, cheatsheets and code.

I won't send you useless inbox filler. No spam, ever.
Unsubscribe at any time.

Read More

Of course, knowing ES6 isn’t much help if the browser doesn’t understand it. And that’s why you need Webpack/Babel:

Related Projects

8 Comments A Quick Tour Of ES6 (Or, The Bits You’ll Actually Use)

Leave a Reply

Your email address will not be published.