CoffeeScript: It's Just JavaScript

Write JavaScript without the fuss.

What is JavaScript?

JavaScript is an implementation of ECMAScript cobbled together during a fever dream in the mid-90s [citation needed].

When implemented in web browsers it let a developer do things like:

confirm('Are you sure you want to leave this page?');

Or,

<img src="CatSitting1.jpg" onMouseOver="this.src='cat-with-mouse.jpg'" onMouseOver="this.src='CatSitting1.jpg'">

Now it's a challenge to find a web application that doesn't use JavaScript for some crucial functionality.

So What's Wrong With It?

It's a very flexible language but has some interesting gotchas.

alert(42 == '42');
alert('a' + 1);
alert('a' - 1);
alert(11 + '1');
alert(11 - '1');

And hoisting can be very confusing! Variables can be set before they're declared so unless you're careful you may lose scope. (From Mozilla Developer Network)

var cells = document.getElementsByTagName('td');

    for(var i=0; i<cells.length; i++){
      var cell = cells[i];
      cell.addEventListener('click', function(){
        cell.style.backgroundColor = '#00F'; // which 'cell' ?
      }, false);
    }

And how do you use all the great OO styles you've learned, but there are no 'classes' in JavaScript, so you can't do this:

class Post {
    function setTitle(title){
        this.title = title;
    }
    function setContent(stuff){
        this.content = stuff;
    }
}

Which exposes one of JavaScript's greatest strengths, functions are first class citizens. So to get the functionality of a class one can do something like:

function Post () {

   }

Post.prototype.setTitle = function(title){
    this.title = title;
}

Post.prototype.setContent = function(stuff){
    this.content = stuff
}

This shows how class instances are functions in JavaScript, and prototypes are used to control inheritance.

Whither CoffeeScript

Invented by Jeremy Ashkenas in 2009,

CoffeeScript gets rid of some of the more trying parts of JavaScript without sacrificing the good stuff.

CoffeeScript compiles to JavaScript, so it can run anywhere JavaScript runs, so you can use it with node.js, the browser, or even embedded systems!

And CoffeeScript is pretty popular (13th on github).

Syntactic Sugar

CoffeeScript takes cues from Python, Ruby, and Haskell.

  • Minimal cruft
  • List comprehensions
  • Whitespace awareness

It can make your code terse, and for most scripts will reduce the lines of code making it easier to write and maintain.

All of the APIs in your JavaScript implementation are there. You can use any JavaScript library, access the DOM, and do whatever useful stuff you do right away.

Doing away with ; and {}

Common code practices use line endings for clarity, CoffeeScript uses them logically as well.

Indentation is used to represent code blocks, as opposed to braces. CoffeeScript is whitespace-aware for function calls as well.

In JavaScript semicolons might allow you to do something like this:

var a = 'Hello World!'; b = a.indexOf('!'); function annoy(){ alert(b);} annoy();

Although usually you don't need to, CoffeeScript is generally more readable:

annoy = ->
  alert b
a = "Hello World!"
b = a.indexOf("!")
annoy()

Control Structures and Booleans (In English)

CoffeeScript increases the readability and clarity of your code by using plain english when it makes sense.

Let's revisit the comparison gotchas from before. Remember that "42" == 42; // true? You need to remember to use ===, and remembering is hard. In CoffeeScript just use is.

Here are some plain English operators:

42 is 43 - 1

42 isnt "42"

black and blue

watch GameOfThrones unless EastboundAndDown is on

Which when compiled to JavaScript become:

42 === 43 - 1;

42 !== "42";

black && blue;

if (EastboundAndDown !== true) {
  watch(GameOfThrones);
}

Notice that in CoffeeScript you don't see any semicolons.

List Comprehensions

Let's make fruit juice in CoffeeScript:

fruits = ['lemon', 'orange', 'apple']
alert "#{fruit} juice" for fruit in fruits

Which compiles to:

var fruit, fruits, _i, _len;
fruits = ['lemon', 'orange', 'apple'];
for (_i = 0, _len = fruits.length; _i &lt; _len; _i++) {
    fruit = fruits[_i];
    alert("" + fruit + " juice");
}

You can also make lists using ranges:

[1..10]

How about every integer divisible by 7 between 0 and 100?

num for num in [1..100] when num % 7 is 0

CoffeeScript's list comprehension are a convenience that works in even the most elderly browsers.

Let's make a function

Functions are first class citizens in JavaScript, and CoffeeScript too. CoffeeScript does a couple of nice things that make using and writing functions easier.

In JavaScript you may have noticed some differences between named and anonymous functions. Namely, named functions get hoisted and anonymous ones don't. This can get really confusing, so CoffeeScript uses anonymous functions and scopes them up for you.

isEven = (num) ->
    num % 2 is 0

Becomes:

var isEven;

isEven = function(num) {
    return num % 2 === 0;
};

Notice CoffeeScript doesn't require you to use the return keyword. The last line of a function will be returned by default. It also allows the context of a function to be closed painlessly.

car = ->
  engine = ->
    engine.running = off
  engine.turnOn = ->
    engine.running = on
  engine.isRunning = ->
    engine.running is on
  engine

a = car()
a.turnOn()
console.log a.isRunning() # true
b = car()
console.log b.isRunning() # false
b.turnOn()
console.log b.isRunning() # true

Classes and inheritance

JavaScript prototypes combined with function closures provide what we normally call classes in other languages. CoffeeScript conveniently gives us a class keyword as well as a few convenience keywords.

class Post
  constructor: (@title) ->

class BlogPost extends Post
  setContent: (words) ->
    @content = words
  getContent: ->
    @content

first = new Post('First post')
console.log first.title
second = new BlogPost('Second post')
second.setContent('...cats')
console.log second.getContent()
var BlogPost, Post, first, second, _ref,
  __hasProp = {}.hasOwnProperty,
  __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };

Post = (function() {
  function Post(title) {
    this.title = title;
  }
  return Post;
})();

BlogPost = (function(_super) {
  __extends(BlogPost, _super);
  function BlogPost() {
    _ref = BlogPost.__super__.constructor.apply(this, arguments);
    return _ref;
  }
  BlogPost.prototype.setContent = function(words) {
    return this.content = words;
  };
  BlogPost.prototype.getContent = function() {
    return this.content;
  };
  return BlogPost;
})(Post);

first = new Post('First post');
console.log(first.title);
second = new BlogPost('Second post');
second.setContent('...cats');
console.log(second.getContent());

Other cool stuff

Destructuring Assignments

Future versions of JavaScript will include syntax to make setting up complex objects and arrays easier. For now you can do it in CoffeeScript like this:

getUser = (id) ->
  # do some AJAX to get user by id
  ['Lars','lars@aol.com','555-1212']

[name, email, phone] = getUser 5

console.log name, email, phone
var email, getUser, name, phone, _ref;

getUser = function(id) {
  return ['Lars', 'lars@aol.com', '555-1212'];
};

_ref = getUser(5), name = _ref[0], email = _ref[1], phone = _ref[2];

console.log(name, email, phone);

Existential Operator

if truck?
  console.log truck
else
  truck = 'car'
var truck;

if (typeof truck !== "undefined" && truck !== null) {
  console.log(truck);
} else {
  truck = 'car';
}

String interpolation

name = 'Lars'

alert "Thanks a bunch #{name}. Also, #{1 + 1}."
var name;

name = 'Lars';

alert("Thanks a bunch " + name + ". Also, " + (1 + 1) + ".");

jQuery, nothing to see here...

Since CoffeeScript is just JavaScript you'll have no problem using jQuery.

Here's a span with id selectme:

Select me!

Some things you can't help

CoffeeScript doesn't try to replace the JavaScript implementation, it merely provides a less fussy way to use it. That means things like this still happen:

[12, 2, 5, 3].sort() # [ 12, 2, 3, 4 ]
[12, 4, 3, 2].sort(); // [ 12, 2, 3, 4 ]

How to use it

There are two main options for using CoffeeScript in a web application. Either use the CoffeeScript compiler to preprocess a .coffee file into JavaScript or use client side compilation.

For production the former is preferable, but the latter makes development easier.

$ sudo npm install -g coffee-script
$ coffee
coffee> console.log 'Hello World!'
Hello World!

Special Topics

CoffeeConsole

You can use CoffeeScript in the Chrome developer console using this extension.

js2coffee

Since CoffeeScript is just JavaScript one can convert JavaScript to CoffeeScript using js2coffee.

Iced CoffeeScript

Jeremy Ashkenas left the door open to easily extend CoffeeScript. One example is Iced CoffeeScript, which adds await and defer keywords to make asynchronous tasks cleaner.

# Search for 'keyword' on twitter, then callback 'cb'
    # with the results found.
    search = (keyword, cb) ->= (keyword, cb) ->
      host = "http://search.twitter.com/"
      url = "#{host}/search.json?q=#{keyword}&callback=?"
      $.getJSON url, (json) ->
        cb json.results


# Search for 'keyword' on twitter, then callback 'cb'
    # with the results found.
    search = (keyword, cb) ->= (keyword, cb) ->
      host = "http://search.twitter.com/"
      url = "#{host}/search.json?q=#{keyword}&callback=?"
      await $.getJSON url, defer json
      cb json.results

Discuss on Hacker News.