home
16 May 2012 - By Karl Seguin

Ten Features I Like About CoffeeScript

String Interpolation

It's pretty common that you'll want to build a string based on one or more variables. With CoffeeScript, variables can cleanly be placed inside of as string:

"level greater than '#{maxPowerLevel}'"
"level greater than '" + maxPowerLevel + "'"

With well-named variables, string interpolation is easier to read and maintain. Of the three main ways to do this (the third being via some type of formatting function like sprintf), string interpolation is by far, the best, for both simple and complex cases.

Statement Modifiers

I'll admit that statement modifiers are a pretty minor thing, but as trivial as they are, I've always found them useful for simple statements:

applyChanges = (elements) ->
  return if elements.length == 0
  ...
applyChanges = function(elements) {
  if (elements.length === 0) { return; }
  ...

Callback Handling

Whether you are doing client-side development or server-side development, callbacks and JavaScript go hand-in-hand. And, although I don't mind braces, I've always found JavaScript's syntax to be far too verbose. CoffeeScript helps in two ways. First, optional parenthesis, which I generally dislike, except when dealing with callbacks. Second, blocks via indentation. Compare:

$('#data').on 'click', 'tr', (e) ->
  id = $(this).data('id')
  ...
$('#data').on('click, 'tr', function(e) {
    id = $(this).data('id')
    ...
  });

I find this particularly useful to help minimize the clutter caused by node.js callback soup. On the downside, it isn't great when you have a parameter after the callback, such as with setInterval or $.get and $.post.

Comprehensions

Looping over arrays and hashes is pretty fundamental, and the more we can enhance the experience, the better. Even if you don't leverage comprehensions as-is, simply having a for loop that exposes the value+index of an array, or the key+value of a hash, is a nice addition.

books = [
  {name: 'Dune', rating: 5}
  {name: "Old Man's War", rating: 4}
  {name: 'Foundation', rating: 3}
]
good = (b.name for b in books when b.rating > 3)
books = [
  {name: 'Dune', rating: 5},
  {name: "Old Man's War", rating: 4},
  {name: 'Foundation', rating: 3}
];
good = [];
for(var i = 0; i < books.length; ++i) {
  if (books[i].rating > 3) {
    good.push(books[i].name);
  }
}

If you'd like learn more about comprehensions, I wrote a blog post on it.

The Fat Arrow

In JavaScript, the meaning of this isn't always intuitive. Consider this CoffeeScript code:

class DataTable
  constructor: (element) ->
    this.element = $(element).on 'click', 'tr', this.clicked

  clicked: (e) ->
    this.highlight()

  highlight: ->
    ....

One might expect that this in the clicked method to be our DataTable instance, but instead, it'll be the row which is clicked. This means that the call to highlight will fail, since the html row element doesn't have a highlight method. That clicked exists as a method of DataTable doesn't mean much.

To solve this, CoffeeScript lets you define a method with a fat arrow =>. Using => binds the function to the expected scope:

class DataTable
  constructor: (element) ->
    this.element = $(element).on 'click', 'tr', this.clicked

  clicked: (e) =>
    this.highlight()

  highlight: =>
    ....
__bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
function DataTable(element) {
  this.highlight = __bind(this.highlight, this);
  this.clicked = __bind(this.clicked, this);
  this.element = $(element).click(this.clicked);
}
DataTable.prototype.clicked = function(e) {
  this.highlight();
  ...
};
DataTable.prototype.highlight = function() {
  ...
};

Sometimes you'll want the normal JavaScript behavior (via a slim arrow), and sometimes you'll want to fat arrow behavior. In general, I find myself using slim arrows more often in client-side code, and fat arrows more often on server-side code.

@ Alias

Speaking of this, CoffeeScript has a shortcut for it, the @ symbol. It's easy to write this off as meaningless syntactical sugar, but it is useful. First, constructor parameters prefixed with @ are converted into properties:

class User
  constructor: (@id) ->
function User(id) {
  this.id = id;
}

Beyond this though, it's a nice way to define class method:

class User
  constructor: (@id) ->

  @findById: (id) =>
     ...
function User(id) {
  this.id = id;
}
User.findById = function() {};

Neither @ nor the fat arrow mean that you don't have to worry about the current meaning of this (or its alias @). They aren't silver bullets, which isn't to say that they don't add value.

Scoping

CoffeeScript takes care of properly scoping your variables. This means that you don't use the var keyword and don't have to worry about having an inner variable shadow an outer one. One top of that, all CoffeeScript output is wrapped inside an anonymous functions, which means you won't leak any global objects.

If you do want to expose something globally, say like our User class above, you need to attach it to a global object, like window. Of course, on the node.js side, you can simply export it.

Clean JavaScript Output

One of the nicest things about CoffeeScript is that it generates clean and readable JavaScript. This makes it both easy to learn and debug. In fact, one of the best ways to learn CoffeeScript is to go to the official website, click the "Try CoffeeScript" button at the top, and type away. Seeing the instant translation to JavaScript is pretty useful.

Classes

We've seen examples of it as a side effect of other features, but CoffeeScript classes are cleaner and more intuitive than their JavaScript counterparts. And the only reason I'm not showing how to inherit via extend is because I don't want to include all the necessary JavaScript...

class User
  constructor: (@id) ->

  @findById: (id) ->

  @findByEmail: (email) ->

  save: =>
function User(id) {
  this.id = id;
  this.save = __bind(this.save, this);
}

User.findById = function(id) {};

User.findByEmail = function(email) {};

User.prototype.save = function() {};

Operators

In CoffeeScript, == and != translate to === and !==, which you probably always want to be using. This alone removes a common pain point.

In addition to that, there's the existential operator. In its simplest form, it checks for undefined and null:

 return null unless name?
if (typeof name === "undefined" || name === null) {
  return null;
}

But it can also be used in chains:

class Unicorn
  is_rookie: () ->
    vampire?.kills > 0
Unicorn.prototype.is_rookie = function() {
  return (typeof vampire !== "undefined" && vampire !== null ? vampire.kills : void 0) > 0;
};

Wrap Up

CoffeeScript has a number of other nice features, and it's always evolving. I understand that some people simply don't like it, which is fine, since a lot of what it offers is subjective. However, I'm baffled that people dismiss as being nothing but syntactical sugar. Almost anything in the programming world can be described as syntactical sugar which sits on top of a more fundamental concept. It's less verbose and improves readability. That's a win in my books.

Of course, as someone who believes that knowing C is critical, I agree that new developers should first learn JavaScript and then spend a day learning CoffeeScript. But that's pretty much as much of a debate as I plan on having about it.

post tag: node.js
blog comments powered by Disqus