With more and more progress being made on ES6, and a steady stream of new feature implementations
showing up in Firefox and Chrome, I thought it might be fun to take some real-world code and
refactor it to take advantage of forthcoming language additions.

These examples are by no means an exhaustive overview of what's new in ES6, and since
the spec isn't complete, a lot of these are subject to change. Nonetheless, it's still
kind of fun to get an idea of what the future of JavaScript looks like.

Disclaimer: I'm not saying that the following examples are necessarily The Right Way™ to refactor in ES6. They're really just meant to be a vehicle for demonstrating some cool new stuff.

_.range

The first example comes from Underscore's _.range method. Here's the original:

_.range = function(start, stop, step) {
  if (arguments.length <= 1) {
    stop = start || 0;
    start = 0;
  }
  step = arguments[2] || 1;

  var len = Math.max(Math.ceil((stop - start) / step), 0);
  var idx = 0;
  var range = new Array(len);

  while(idx < len) {
    range[idx++] = start;
    start += step;
  }

  return range;
};

The thing I love about this example is that the problem is so straight-forward. It's hard to imagine
anyone taking a drastically different approach here. Sure, some people might not initialize that
range array with a length, and would instead rely on pushing values. But ultimately, the basic mechanism is going to be the same.

What does stand out though, at least to me, is all that boilerplate in the first few lines of the function
body. This is a pretty common sight in JavaScript for polymorphic methods. There's really never been
a particularly elegant way to deal with a variable number of arguments.

Fortunately, this is one area where ES6 really comes through. Here's the same method, refactored
to take advantage of some of ECMAScript's forthcoming features:

_.range = function(start = 0, stop = start, step = 1) {
    var len = Math.max(Math.ceil((stop - start) / step), 0),
        range = new Array(len);
    for (let idx = 0; idx < len; ++idx) {
      [range[idx], start] = [start, start += step];
    }
    return range;
};

The first thing you'll notice is that we've got default parameters. Because I can define
defaults, I was able to remove 5 lines of code right off the bat. Not only that, but I think
this syntax is also significantly more explicit, especially once you get used to it.

Second change is the use of the let statement in the loop. let is similar to var, except it has
block scope rather than function scope. In this case, there's honestly not much of a difference, but it
seemed worth demonstrating - especially since let is already implemented in Firefox, Chrome, and IE11.
In this case, the main advantage is basically cosmetic. Since idx is only important to me inside that loop,
I can scope it accordingly to reflect that.

The third change is the destructiring assignment inside the loop. I'm not going to dive into that, because
my explanation wouldn't be nearly as good as what you'll get by reading This post
by Nick Fitzgerald. And you should definitely read that. Immediately.

Really. Go ahead, I'll wait.

Backbone.Model

Oh good, you're back.

Next example comes from Backbone's Backbone.Model:

var Model = Backbone.Model = function(attributes, options) {
    var defaults;
    var attrs = attributes || {};
    options || (options = {});
    this.cid = _.uniqueId('c');
    this.attributes = {};
    _.extend(this, _.pick(options, modelOptions));
    if (options.parse) attrs = this.parse(attrs, options) || {};
    if (defaults = _.result(this, 'defaults')) {
        attrs = _.defaults({}, attrs, defaults);
    }
    this.set(attrs, options);
    this.changed = {};
    this.initialize.apply(this, arguments);
};

This is a simple example, but also a pretty important one. Defining "classes" in JavaScript
has always been a little bit awkward. First, you write a function (and probably capitalize the first
letter to indicate that it's a constructor, though this is just a convention). Then, completely outside
of that function definition, you start adding methods to its prototype object. It just looks sort of messy,
and fails to really convey what your intention is.

This is where ES6 classes come in:

var Model = Backbone.Model = class Model {
    constructor (attrs = {}, options = {}) {
        this.cid = _.unique('c');
        this.attributes = {};
        _.extend(this, _.pick(options, modelOptions));
        if (options.parse) attrs = this.parse(attrs, options) || {};
        if (defaults = _.result(this, 'defaults')) {
            attrs = _.defaults({}, attrs, defaults);
        }
        this.set(attrs, options);
        this.changed = {};
        this.initialize.apply(this, arguments);
    }
};

Not too much has really changed here, except for the fact that we're substituting that anonymous
constructor function with a class definition. Then we just move the original function body inside
of the constructor definition and pretty much call it a day, aside from swapping out some boilerplate
for default params.

The beauty here is that (a) this is a lot more explicit, and (b) we can also put our methods
inside the class definition, like this:

var Model = Backbone.Model = class Model {
    constructor (attrs = {}, options = {}) {
        ...
    }
    sync() {
        return Backbone.sync.apply(this, arguments);
    }
    get(attr) {
        return this.attributes[attr];
    }
};

So now we're being a lot clearer about the intent of the code. The constructor and method definitions
are all living in the same place. It's easier to read and understand what's going on.

Backbone.Collection#toJSON

This final example is a really small one, but it lets me demonstrate one of my favorite ES6 features.

Here's the original code for Backbone.Collection#toJSON:

toJSON: function(options) {
    return this.map(function(model){ return model.toJSON(options); });
}

That inner line, where we pass an anonymous one-line function to map is something that happens
all the time in JavaScript.

Soon, we'll have an easier way:

toJSON2: function(options) {
    return this.map(model => model.toJSON(options));
}

If you've ever used CoffeeScript, this should look pretty familiar. If not, that's called a fat-arrow function.

Basically, it's shorthand syntax for an anonymous function that returns a value. It doesn't need a function keyword,
the parens are optional when there's a single parameter, the value of this is bound to the containing scope,
and automatically return the value of the expression following the fat arrow.

Wrapping up

The point here wasn't really to give a comprehensive or in-depth overview of everything in ES6. It's really
just to highlight a few interesting changes in the context of how they might be used in the real world. I highly
recommend reading the proposals and trying this
stuff out for yourself.

If that sounds fun, check out Google's Traceur Compiler, which lets
you write ES6 and compile it into valid ES5. They even have an online version
that's great for quick experiments, or if (like me) you're just too lazy to download the code.