CoffeeScripters, Have You Tried ES6 Yet?

ES6 is providing a lot of features that I’ve heard CoffeeScripters bragging about for the last few years, but is it enough for people to start considering vanilla JavaScript to be a viable alternative? Let’s take a look at some of the big selling points of CoffeeScript and see how they compare to some of the new hotness in ES6.

Classes

The first and most obvious of these features is classes. ES6 supports them natively and we even get inheritance and super!

Here is some CoffeeScript from the class examples on coffeescript.org:

class Animal
  constructor: (@name) ->

  move: (meters) ->
    alert @name + " moved #{meters}m."

class Snake extends Animal
  move: ->
    alert "Slithering..."
    super 5

class Horse extends Animal
  move: ->
    alert "Galloping..."
    super 45

sam = new Snake "Sammy the Python"
tom = new Horse "Tommy the Palomino"

sam.move()
tom.move()

Alright, now try to look past the curly braces and semicolons (which are optional in most cases) and make note of the similarities with what’s available in ES6 now. This functions the exact same as the CoffeeScript example, but is pure JavaScript:

class Animal {
  constructor(name) {
    this.name = name;
  }

  move(meters) {
    alert(`${this.name} moved ${meters}m.`);
  }
}

class Snake extends Animal {
  move() {
    alert("Slithering...");
    super(5);
  }
}

class Horse extends Animal {
  move() {
    alert("Galloping...");
    super(45);
  }
}

var sam = new Snake("Sammy the Python");
var tom = new Horse("Tommy the Palomino");

sam.move();
tom.move();

This example shows off classes, inheritance, the use of super and string interpolation. These are some of the major features of CoffeeScript that are now available natively in pure JavaScript.

Arrow Functions

There is a new arrow function syntax in ES6 which lexically binds this, providing a convenient syntax for declaring functions that capture the this value of the enclosing context. This is the equivalent of the “fat arrow” (=>) in CoffeeScript.

Here is another example from coffeescript.org:

Account = (customer, cart) ->
  @customer = customer
  @cart = cart

  $('.shopping_cart').bind 'click', (event) =>
    @customer.purchase @cart

Here is what this translates to in JavaScript:

var Account = function(customer, cart) {
  this.customer = customer;
  this.cart = cart;

  $('.shopping_cart').bind('click', (event) => {
    this.customer.purchase this.cart;
  });
}

The differences in this example are a little more glaring, mostly because ES6 doesn’t support the “skinny arrow” syntax (->). However, I don’t see this as a big downside. Most of the time I’m wanting to pass this rather than override it and if I really don’t want a lexically bound this in a function I’d want to think about why that’s the case. It could be considered a code smell for poorly structured code.

Splats and Spreads

CoffeeScript has the splat operator which can be used to make things like handling a variable number of arguments in a function a lot easier.

gold = silver = rest = "unknown"

awardMedals = (first, second, others...) ->
  gold   = first
  silver = second
  rest   = others

contenders = [
  "Michael Phelps"
  "Liu Xiang"
  "Yao Ming"
  "Allyson Felix"
  "Shawn Johnson"
  "Roman Sebrle"
  "Guo Jingjing"
  "Tyson Gay"
  "Asafa Powell"
  "Usain Bolt"
]

awardMedals contenders...

alert "Gold: " + gold
alert "Silver: " + silver
alert "The Field: " + rest

ES6 has the spread operator which basically does the same thing. The syntax is almost the same, but the ellipses goes at the beginning of the argument name:

var gold = silver = rest = "unknown";

var awardMedals = function(first, second, ...others) {
  gold   = first;
  silver = second;
  rest   = others;
}

var contenders = [
  "Michael Phelps",
  "Liu Xiang",
  "Yao Ming",
  "Allyson Felix",
  "Shawn Johnson",
  "Roman Sebrle",
  "Guo Jingjing",
  "Tyson Gay",
  "Asafa Powell",
  "Usain Bolt"
];

awardMedals(...contenders);

alert(`Gold: ${gold}`);
alert(`Silver: ${silver}`);
alert(`The Field: ${rest}`);

Destructured Assignment

CofeeScript says outright on their homepage that they implemented this feature straight from the spec so this works exactly how you would expect it to.

Here is the CoffeeScript:

# variable swapping
theBait   = 1000
theSwitch = 0

[theBait, theSwitch] = [theSwitch, theBait]

# handling multiple return values
weatherReport = (location) ->
  # Make an Ajax request to fetch the weather...
  [location, 72, "Mostly Sunny"]

[city, temp, forecast] = weatherReport "Berkeley, CA"

And the JavaScript:

// variable swapping
var theBait   = 1000;
var theSwitch = 0;

[theBait, theSwitch] = [theSwitch, theBait];

// handling multiple return values
var weatherReport = function(location) {
  // Make an Ajax request to fetch the weather...
  [location, 72, "Mostly Sunny"];
}

[city, temp, forecast] = weatherReport "Berkeley, CA";

String Interpolation

We already saw a taste of this in the classes example, but here is a full comparison:

Here is the CoffeeScript:

author = "Wittgenstein"
quote  = "A picture is a fact. -- #{ author }"

sentence = "#{ 22 / 7 } is a decent approximation of π"

And the JavaScript:

var author = `Wittgenstein`;
var quote  = `A picture is a fact. -- ${ author }`;

var sentence = `${ 22 / 7 } is a decent approximation of π`;

Default Parameters

In ES6 you no longer need to use the a = a || 'b' hack for assigning default values to parameters. It works the exact same way CoffeeScript’s default parameter value assignment works:

fill = (container, liquid = "coffee") ->
  "Filling the #{container} with #{liquid}..."

Here it is in JavaScript:

var fill = function(container, liquid = "coffee") {
  return "Filling the #{container} with #{liquid}...";
}

The Bottom Line

CoffeeScript still does provide a lot of value for people who aren’t fans of certain JavaScript syntax. However, the value proposition for using something like CoffeeScript is diminishing rapidly as we get access to new ES6 features. Things like Traceur and 6to5 enable us to start using these features today, and for someone like me who is mainly writing JavaScript right now, the cons outweigh the pros for using a tool like CoffeeScript.

My goal is to write code that is maintainable and takes advantage of the latest features of the language. Using the new syntax now along with a transpiler means that I can remove the transpilation build step in my projects one day when browsers have implemented the features I’m utilizing without changing any of my source.

“But CoffeeScript IS JavaScript!”

I’ve heard from a lot of people that at some point they do plan on abandoning CoffeeScript. Their plan for transitioning is usually something like this:

I’ll just compile my CoffeeScript and swap out the .coffee
for my .js files.

Based on the compilation results I’ve seen, I don’t think CoffeeScript produces code anyone would want to maintain as vanilla JavaScript, especially if they have spent the last few years writing CoffeeScript. If you have given any thought to abandoning CoffeeScript at some point, I strongly encourage you to give JavaScript with transpilation for ES6 features a shot.

  • Capaj

    I have never been a fan of Cofeescript, so I really welcome this article. My brain just can’t read it well enough when few words on a line could be a statement or a function call at the same time.

    • Yeah, it never sat right with me either, but I still think it’s a super valuable tool that can help take away some of the rough edges in JS. However, I think being able to use vanilla JS with some of the ES6/7 features and syntax probably makes it palatable for some long time CoffeeScript users who were previously frustrated with some of the limitations, syntax and quirks of JS.

  • David Souther

    This article misses the bigger picture – the Coffee maintainers have been very hesitant to consider moving Coffee forward with TC39 changes, while TC39 has purloined many great features from Coffee.

    • Yeah, the relationship with the standards groups and languages like CS that get compiled down to native code are really interesting. They definitely provide a ton of value and give the community a chance to express opinions on features which ultimately end up shaping the future specs for the language they are built on top of.

      The point I was trying to make with this article was mainly that JavaScript is evolving fast and that people who might have written it off in the past because it didn’t fit into their classical OO mindset or couldn’t stand some of the quirks or missing features from their favorite languages should reconsider their choice. Sorry if you thought I missed an important argument here. I would love to hear more about what you think we should be discussing regarding the relationship between JS and CS, or any of the languages that compile to JS.

      Thanks for taking the time to give this a read and share your thoughts, @David Souther:disqus!

  • Jacob Kiser

    Sorry Schmitty, but I have to disagree with you. While I mainly work in typescript it isn’t dissimilar from Coffee. If you’re writing an angular application, jquery plugin, or doing some small dom manipulation javascript will do just fine. If you have a massive MEAN stack with all the whistles and bells you are in the neighborhood of criminally negligent for not using TS or CS.

    The biggest gap in this argument has come from the fact that JS is being used in large and small scale applications with the majority of developers being on the small side. Using one of these super sets usually turns out to be a task with few “visible” benefits because there is no telling how many times it has saved someone’s ass behind the scenes. But ask yourself this… would you tell a PHP guy to just go ahead and not use static typing in his server side code? Obviously not, so why advocate it in js? I’ll give you that well written angular doesn’t need it, but node?

    Now, I’m a bit late to the game on this article and at this point you can just go write ES6. However, You can also directly convert your TS to ES6 with –target es6.

    Nice Blog and enjoy your articles … Your Truly Deadlock

    • @jacob_kiser:disqus, what do you feel CoffeeScript protects you from? I hear the argument for TS, but not sure I agree on CS… If my goal was to protect people from writing crappy JavaScript, I would be in favor of integrating tooling like Flow and linting into our CI (and maybe even use TS) rather than use CS.

      Thanks for sharing, Deadlock! Hope to catch up soon!

      • Jacob Kiser

        Sorry El-Schmidtarino… I always thought CS was statically typed like TS as I had only ever seen it written as Typed-Coffe-Script. So, I guess CS is pretty much useless after all. I guess that is the nature of working for “the man”.

        Anyway, I’m seeing a lot of resistance to static typing and the TS like nature of ES6 and I think it’s a bad thing for a lot of developers. JS is starting to be everywhere and large projects / companies aren’t going to stand for writing JS the “old” way. There will come a time when people who stick to that mentality will be limited to working on local small websites if they don’t adapt.

        With golf season upon us we’ll have to discuss this further on the course as I watch you cheat over a round.

        • Haha, it’s OK, Deadlock. Just glad that you agree CS is useless ;)

          I agree with you on the benefits of static typing, and I’m excited to see a general move toward strict languages in web development. It’s something relatively easy we can adopt to build better applications.

          Do you have the same feelings about testing? I see a lot of resistance to that from the same communities resisting static typing.

          Would love to discuss this further on the golf course. As soon as this snow is gone we should get out there!