Our team recently decided to try out CoffeeScript. The Rails asset pipeline makes this so simple, and since CoffeeScript makes JavaScript look like Ruby, it seemed like a good fit.

It took a few days of working with CoffeeScript, compiling it to JavaScript and checking the results to really get the hang of it (Coffeescript.org is great for this, since you can compile CS right on the “Try CoffeeScript” tab). But it was worth it - CS gets out of your way and lets you get to the code, and more importantly, it’s easier to read.

What tripped me up at first

Function arguments

Parentheses are optional when making functions calls - ALMOST! If you’re calling a function with no arguments, you must end with (), otherwise CS can’t tell the difference between a function pointer and a function to execute:

myObject =
  doSomething: ->
    alert "I'm doing it!"

  askSomething: (question) ->
    confirm question

# This works
myObject.doSomething()

# This doesn't
myObject.doSomething # do you want to execute doSomething or return a pointer to it?

# Parentheses can be left out if you're passing arguments
myObject.askSomething "Buddy the elf, what's your favorite color?"

The “super” keyword

Since it wasn’t long ago that I was working in C#, I expected the CS super keyword to work like base in C# - a reference to the base (super) class. Nope! It simply refers to the method that you are overriding:

app = app or {}
class app.Person  
  constructor: (@name, @address) ->

  alertInfo: ->
    info = @getInfo()
    alert info

  getInfo: ->
    "name: #{@name}, address: #{@address}"

class app.Employee extends app.Person

  constructor: (name, address, @company) ->
    super(name, address)

  getInfo: ->
    info = super # this calls getInfo on app.Person
    info + ", company: #{@company}"

p = new app.Person('Ronald McDonald', 'Clown house')
p.alertInfo() 
# alerts "name: Ronald McDonald, address: Clown house"

emp = new app.Employee('Tom Jones', 'Vegas', 'TJ, Inc')
emp.alertInfo()
# alerts "name: Tom Jones, address: Vegas, company: TJ, Inc"

The good parts

“Fat arrow” functions

Understanding the scope of ‘this’ can be problematic in JavaScript. Using => when declaring functions helps by using a variable holding this when the object was constructed.

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

  $('.shopping_cart').bind 'click', (event) =>
    # fat arrow syntax makes sure we have the right cart!
    @customer.purchase @cart

Compiles to this JS:

var Account;

Account = function(customer, cart) {
  var _this = this;
  this.customer = customer;
  this.cart = cart;
  return $('.shopping_cart').bind('click', function(event) {
    return _this.customer.purchase(_this.cart);
  });
};

String interpolation

Every language should support Ruby style string interpolation. CoffeeScript adds support for simple replacements and complete expression evaluation:

formTitle = "#{if @isNew() then 'New' else 'Edit'} organization"

Default values for arguments

Arguments can be assigned a default value that can be overridden by passing in any non-null value when calling the method.

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

Compiles to this JS:

var fill;

fill = function(container, liquid) {
  if (liquid == null) {
    liquid = "coffee";
  }
  return "Filling the " + container + " with " + liquid + "...";
};

Existential operator

I was working in Grails a few months back, and Groovy was my first exposure to an existential operator. Now I wish all languages had it, since it makes working with nested objects MUCH easier! Using a simple ? after each property gives you a safe check against null:

zip = lottery.drawWinner?().address?.zipcode

Compiles to this JS:

var zip, _ref;

zip = typeof lottery.drawWinner === "function" ? 
      (_ref = lottery.drawWinner().address) != null ? _ref.zipcode : void 0 : void 0;

The bad parts

Indentation scoping

CoffeeScript uses indentation instead of braces {} for functions, if/else blocks, etc… In a functional language it’s common to see several nested functions, and simply relying on indentation makes it easy to create subtle scoping issues in the code. RubyMine/WebStorm definitely help in this area with a faint vertical line to mark each indentation level, but it would be nice if braces were supported as an option. After all, “it’s just JavaScript”, right?

Conclusion

I hope this article helps someone else getting started. Be sure to check out the Coffeescript.org website - the documentation is great with loads of examples, many I’ve used here.

  • Categories
comments powered by Disqus