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.
Source: