I’ve been a CoffeeScript fan for over two years now. I find I’m more productive writing CoffeeScript, make less silly mistakes, and with support for source maps debugging CoffeeScript code is completely painless.

What's New in ES6? Perspective of a CoffeeScript Convert

Recently I’ve been playing with ES6 using Babel, and overall I’m a fan. In this article, I will compile my findings on ES6 from the perspective of a CoffeeScript convert, look at the things I love and see what things I’m still missing.

Syntactically Significant Indentation: a Boon or a Bane?

I’ve always had a bit of a love/hate relationship with CoffeeScript’s syntactically significant indentation. When everything goes fine you get the “Look ma, no hands!” bragging rights of using such a super-minimalistic syntax. But when things go wrong and incorrect indentation causes real bugs (trust me, it happens), it doesn’t feel like the benefits are worthwhile.

 if iWriteCoffeeScript
   if iAmNotCareful
      badThings = 'happen'

Typically these bugs are caused when you have a block of code with a few nested statements, and you accidentally indent one statement incorrectly. Yes it’s usually caused by tired eyes, but it’s much more likely to happen in CoffeeScript in my opinion.

When I started writing ES6 I wasn’t quite sure what my gut reaction would be. It turns out it actually felt really nice to start using curly braces again. Using a modern editor also helps, as generally you only have to open the brace and your editor is kind enough to close it for you. It felt a bit like returning to a calm, clear universe after the voodoo magic of CoffeeScript.

if (iWriteES6) {
  if (iWriteNestedStatements) {
     let badThings = 'are less likely to happen'

Of course I insist on throwing out the semicolons. If we don’t need them, I say throw them out. I find them ugly and it’s extra typing.

Class Support

The class support in ES6 is fantastic, and if you’re moving from CoffeeScript you will feel right at home. Let’s compare the syntax between the two with a simple example:

ES6 Class

class Animal {
  constructor(numberOfLegs) {
    this.numberOfLegs = numberOfLegs
  toString() {
    return `I am an animal with ${this.numberOfLegs} legs`

class Monkey extends Animal {
  constructor(bananas) {
    this.bananas = bananas
  toString() {
    let superString = super.toString()
      .replace(/an animal/, 'a monkey')
    return `${superString} and ${this.bananas} bananas`

CoffeeScript Class

class Animal
  constructor: (@numberOfLegs) ->

  toString: ->
    "I am an animal with #{@numberOfLegs} legs"

class Monkey extends Animal
   constructor: (@numberOfBananas) ->

   toString: ->
     superString = super.toString()
       .replace(/an animal/, 'a monkey')
     "#{superString} and #{@numberOfLegs} bananas"

First Impressions

The first thing that you may notice is that ES6 is still a lot more verbose than CoffeeScript. One shortcut that is very handy in CoffeeScript is the support for automatic assignment of instance variables in the constructor:

constructor: (@numberOfLegs) ->

This is equivalent to the following in ES6:

 constructor(numberOfLegs) {
    this.numberOfLegs = numberOfLegs

Of course that’s not really the end of the world, and if anything this more explicit syntax is easier to read. Another small thing missing is that we have no @ shortcut for this, which always felt nice coming from a Ruby background, but again isn’t a massive deal-breaker. In general we feel pretty much at home here, and I actually prefer the ES6 syntax for defining methods.

How Super!

There is a small gotcha with the way super is handled in ES6. CoffeeScript handles super in the Ruby way (“please send a message to the superclass method of the same name”), but the only time you can use a “naked” super in ES6 is in the constructor. ES6 follows a more conventional Java-like approach where super is a reference to the superclass instance.

I Will Return

In CoffeeScript we can use implicit returns to build beautiful code like the following:

foo = ->
  if Math.random() > 0.5
    if Math.random() > 0.5
      if Math.random() > 0.5

Here you have a very small chance of getting “foo” back when you call foo(). ES6 is having none of this and forces us to return using the return keyword:

const foo = () => {
  if (Math.random() > 0.5 ) {
    if (Math.random() > 0.5 ) {
      if (Math.random() > 0.5 ) {
        return "foo"

As we can see in the above example, this is generally a good thing, but I still find myself forgetting to add it and getting unexpected undefined returns all over the place! There is one exception I’ve come across, concerning Arrow Functions, where you get an implicit return for one liners like this:

array.map( x => x * 10 )

This is kind of handy but can be slightly confusing as you need the return if you add the curly braces:

array.map( x => {
  return x * 10

However, it still makes sense. The reasoning being that if you have added the curly braces it’s because you want to use multiple lines, and if you have multiple lines it makes sense to be clear what you are returning.

ES6 Class Syntax Bonuses

We’ve seen that CoffeeScript has a few syntactical tricks up its sleeve when it comes to defining classes, but ES6 also has a few tricks of its own.

Getters and Setters

ES6 has powerful support for encapsulation via getters and setters, as illustrated in the following example:

class BananaStore {
  constructor() {
    this._bananas = []
  populate() {
    // fetch our bananas from the backend here
  get bananas() {
    return this._bananas.filter( banana => banana.isRipe )
  set bananas(bananas) {
    if (bananas.length > 100) {
      throw `Wow ${bananas.length} is a lot of bananas!`
    this._bananas = bananas

Thanks to the getter, if we access bananaStore.bananas it will only return ripe bananas. This is really great as in CoffeeScript we would need to implement this via a getter method like bananaStore.getBananas(). This doesn’t feel at all natural when in JavaScript we are used to accessing properties directly. It also makes development confusing because we need to think for each property how we should access it - is it .bananas or getBananas()?

The setter is equally useful as we can clean up the data set or even throw an exception when invalid data is set, as shown in the toy example above. This will be very familiar to anyone from a Ruby background.

I personally don’t think this means you should go crazy using getters and setters for everything. If you can gain something by encapsulating your instance variables via getters and setters (like in the example above) then go ahead and do so. But imagine you just have a boolean flag such as isEditable. If you would not lose anything by directly exposing the isEditable property, I would argue that’s the best approach, as it’s the simplest.

Static methods

ES6 has support for static methods, which lets us do this naughty trick:

class Foo {  
  static new() {
    return new this

Now if you hate writing new Foo() you can now write Foo.new(). Purists will grumble since new is a reserved word, but after a very quick test it seems to work in Node.js and with modern browsers just fine. But you probably don’t want to use this in production!

Unfortunately because there is no support for static properties in ES6, if you want to define class constants and access them in your static method you’d have to do something like this, which is a bit hackish:

class Foo {
  static imgPath() {
    return `${this.ROOT_PATH}/img`
Foo.ROOT_PATH = '/foo'

There is an ES7 Proposal to implement declarative instance and class properties, so that we can do something like this, which would be nice:

class Foo {
  static ROOT_PATH = '/foo'
  static imgPath() {
    return `${this.ROOT_PATH}/img`

This can already be done quite elegantly in CoffeeScript:

class Foo
   @ROOT_PATH: '/foo'

   @imgPath: ->

String Interpolation

The time has come when we can finally do string interpolation in Javascript!

`I am so happy that in the year ${new Date().getFullYear()} we can interpolate strings`

Coming from CoffeeScript/Ruby, this syntax feels a bit yuk. Perhaps because of my Ruby background where a backtick is used to execute system commands, it feels pretty wrong at first. Also using the dollar sign feels kind of eighties – but maybe that’s just me.

I suppose due to backwards compatibility concerns it just wasn’t possible to implement CoffeeScript style string interpolation. "What a #{expletive} shame".

Arrow Functions

Arrow functions are taken for granted in CoffeeScripter. ES6 pretty much implemented the fat arrow syntax of CoffeeScript. So we get a nice short syntax, and we get the lexically scoped this, so we don’t have to jump through hoops like this when using ES5:

var that = this
doSomethingAsync().then( function(res) {

The equivalent in ES6 is:

doSomethingAsync().then( res => {

Notice how I left out the parenthesis around the unary callback, because I can!

Object Literals on Steroids

Object literals in ES6 have some serious performance enhancements. They can do all kinds of things these days!

Dynamic Property Names

I didn’t actually realise until writing this article that since CoffeeScript 1.9.1 we can now do this:

dynamicProperty = 'foo'
obj = {"#{dynamicProperty}": 'bar'}

Which is much less of a pain than something like this which was necessary before:

dynamicProperty = 'foo'
obj = {}
obj[dynamicProperty] = 'bar'

ES6 has an alternative syntax which I think is pretty nice, but not a huge win:

let dynamicProperty = 'foo'
let obj = { [dynamicProperty]: 'bar' }

Funky Shortcuts

This is actually taken from CoffeeScript, but it was something I was ignorant of until now. It’s very useful anyhow:

let foo = 'foo'
let bar = 'bar'
let obj = { foo, bar }

Now the contents of obj is { foo: 'foo', bar: 'bar' }. That’s super useful, and worth remembering.

Everything a Class Can Do I Can Do Too!

Object literals can now do pretty much everything a class can do, even something like:

let obj = {
  _foo: 'foo',
  get foo() {
    return this._foo
  set foo(str) {
     this._foo = str
  isFoo() {
     return this.foo === 'foo'

Not quite sure why you would want to start doing that but hey, now you can! You can’t define static methods of course… as that would make no sense at all.

For-loops to Confuse You

The ES6 for-loop syntax is going to introduce some nice muscle memory bugs for experienced CoffeeScripters, as the for-of of ES6 translates to for-in of CoffeeSCript, and vice-versa.


for (let i of [1, 2, 3]) {
// 1
// 2
// 3


for i of [1, 2, 3]
# 0
# 1
# 2

Should I Switch to ES6?

I’ve currently been working on a fairly large Node.js project using 100% CoffeeScript, and I’m still very happy and super productive with it. I would say that the only thing I am really jealous of in ES6 are the getters and setters.

Also it is still slightly painful using ES6 in practice today. If you are able to use the very latest Node.js version, then you get nearly all ES6 features, but for older versions and in the browser things are still less rosy. Yes, you can use Babel, but of course that means integrating Babel into your stack.

Having said that I can see ES6 over the next year or two gaining a lot of ground, and I hope to see even greater things in ES7.

What I’m really pleased about with ES6 is that “plain old JavaScript” is nearly as friendly and powerful out-of-the-box as CoffeeScript. This is great for me as a freelancer – in the past I used to be a little bit averse to working on JavaScript projects – but with ES6 everything just seems that little bit shinier.

About the author

William Coates, Portugal
member since September 6, 2015
William is a highly skilled full-stack developer and entrepreneur with fifteen years of experience working with web technologies. He loves to keep up to date with the latest tech, and has a real passion for the industry. [click to continue...]
Hiring? Meet the Top 10 Freelance JavaScript Developers for Hire in April 2019


ES6 making JavaScript stating a real usable language at last! Just embrace the ; it makes for easier reading! :)
Eduardo Dias da Costa
Have you heard about Haxe? It is another powerful alternative :) www.haxe.org
daniel hordern
Love the ; & {}. I'll def play with es6
Jorge Primata
smells like C#
Wout Mertens
I was an avid coffeescripter but I was unhappy with the standstill in some areas like https://github.com/jashkenas/coffeescript/issues/3900 and https://github.com/jashkenas/coffeescript/issues/3894 . Then I started using ES6/7 because of coworkers using React and not grokking CS, and I'm a convert. Yes, the syntax is still not as elegant, but the main pain points are addressed, and most importantly the tooling is so much better, it is simply a much larger ecosystem. I really like https://github.com/sindresorhus/xo for keeping code clean and catching subtle bugs, for example. In atom there is a linter plugin for it, with a fix command for automatically cleaning up some things.
Wout Mertens
No real need for ; http://blog.izs.me/post/2353458699/an-open-letter-to-javascript-leaders-regarding
Francis Kim
ES6 is definitely the way to go these days
I am currently an avid CoffeeScripter and I want to be in your position, I want to like ES6, but JavaScript is still too ugly. Trying to read JS (even new ES6 syntax) makes me depressed.
The problem isn't ES6. The problem is Coffee-script needs to compile to ES6 and to fill out whats missing in CS such as getters and setters. Its sad we have to trade what we love for things that we have to progressive.
comments powered by Disqus
Free email updates
Get the latest content first.
No spam. Just great articles & insights.
Free email updates
Get the latest content first.
Thank you for subscribing!
Check your inbox to confirm subscription. You'll start receiving posts after you confirm.
Trending articles
Relevant Technologies
About the author
William Coates
JavaScript Developer
William is a highly skilled full-stack developer and entrepreneur with fifteen years of experience working with web technologies. He loves to keep up to date with the latest tech, and has a real passion for the industry.