CoffeeScript Tricks

These are some of the tricks I use from time to time to improve the code readability and/or speed things up.

Contents

Immediately-invoked function expression (IIFE)

This might not be such a 'big' trick, but here's the syntax:

((args...) ->

) args...

Yeah, nothing too spectacular here. Moving on.

Iterating arrays in reverse

This one might also be obvious to most, but here it is anyway:

a = [1, 2, 3, 4]

for i in a by -1
  console.log i

Getting the last item in an array

Most people would think:

a[a.length - 1]

But there's a slightly better way:

a[-1..][0]

And, no, a[-1] doesn't work. And, yes, you can still use a.pop() if you don't care about what happens to a.

Return or throw

This is one of those patterns that look so obvious when written in CoffeeScript, and yet it compiles into something quite delicious and hackish.

a = () ->
  # calculate x
  x or throw new Error 'OMFG!'

You just have to see what it looks like when compiled:

var a;

a = function() {
  return x || (function() {
    throw new Error('OMFG!');
  })();
};

Deluxe list comprehensions

Let's say we have an array of objects, and we want to transform that object in some way and create a new array or simply overwrite the old one.

Let's say the array looks like this:

a = [
  {a: 2, b: 4}
  {a: 3, b: 5}
  {a: 12, b: 0}
]

Now let's add a c key that will be sum of keys a and b:

a = (
  ((o) -> 
    a: o.a
    b: o.b
    c: o.a + o.b
  ) o for o in a
)

The a = (expression for o in a) is a classic list comprehension. For the expression we use an IIFE which, extracted, looks like this:

(o) -> 
  a: o.a
  b: o.b
  c: o.a + o.b
) o

You can exclude the outer brackets of the list comprehensions provided that you do not change the relative position in code of the actual comprehension. The code below compiles to identical JavaScript as the code above:

a =  # <-- look, mommy, no bracket!
  ((o) -> 
    a: o.a
    b: o.b
    c: o.a + o.b
  ) o for o in a

The reason CoffeeScript compiler works like this is beyond me, so don't ask. The above form is a bit confusing, though, so let's just use brackets when assigning to variables.

You're also guessing correctly that we can also use the deluxe version of when in the comprehensions:

a =  # <-- look, mommy, no bracket!
  ((o) -> 
    a: o.a
    b: o.b
    c: o.a + o.b
  ) o for o in a when ((o) ->
    o.a > o.b
  ) o

When using the deluxe list comprehensions as a return value, you don't need to include the outer brackets.

c = () ->
  ((o) -> 
    a: o.a
    b: o.b
    c: o.a + o.b
  ) o for o in a  # <-- look, mommy, no brackets!

When using as return value, I think it's ok to just omit the brackets. It doesn't make any difference at all.

Recursive function as object property

You can use a recursive function as an object property. The problem is usually how to call this function from within itself when it's being assigned as a property. Take a look at this:

a =
  b: () ->

How do you call a.b from within itself? Well, you can simply call it as @b()

a =
  b: (i, n=0) ->
    return n if not i
    @b(i - 1, n + i)

What if we want to use b stand-alone? For example, we want to do something like:

b = a.b
b 12

In this case, we need b to refer to itsef using the reference to a:

a =
  b: (i, n=0) ->
    return n if not i
    a.b(i - 1, n + i)

Alternatively, you can also do this:

a =
  b: b = (i, n=0) ->
    return n if not i
    b(i - 1, n + i)  # <-- `b` refers to `b` variable in same scope as `a`

This makes b both a's property, and a stand-alone b variable within the same scope where a is defined (this works in JavaScript as well).

UMD wrapper

If you want to create an UMD wrapper for your AMD module, there are many ways to do it, including using IIFE, which is a recommendation by James Burke. Here, we'll take a look at an alternative syntax which works well in CoffeeScript when using a CommonJS-style module that calls require.

The UMD wrapper below supports browsers without AMD loaders, as well as NodeJS modules.

define = ((root) ->
  if typeof root.define is 'function' and root.define.amd
    root.define  # Return RequireJS's version of define
  else
    if typeof module is 'object' and module.exports
      (factory) -> module.exports = factory(root.require)
    else
      require = (dep) ->
        (() ->
          switch dep
            when 'underscore' then root._
            else null
        )() or throw new Error "Unmet dependency #{dep}"
      (factory) -> root.myModule = factory(require)
) this

define (require) ->
  _ = require 'underscore'

The above wrapper may seem huge, but it actually takes care of dependencies and also traps any missing dependencies when working with non-AMD browser situation.

If the module has no dependencies, it can be simplified:

define = ((root) ->
  if typeof root.define is 'function' and root.define.amd
    root.define  # Return RequireJS's version of define
  else
    if typeof module is 'object' and module.exports
      (factory) -> module.exports = factory()
    else
      (factory) -> root.myModule = factory()
) this

define () ->

Further, if you do not need support for NodeJS, it's even simpler:

define = ((root) ->
  if typeof root.define is 'function' and root.define.amd
    root.define  # Return RequireJS's version of define
  else
    (factory) -> root.myModule = factory()
) this

define () ->

Last but not least, if you do need dependencies, but don't need NodeJS:

define = ((root) ->
  if typeof root.define is 'function' and root.define.amd
    root.define  # Return RequireJS's version of define
  else
    require = (dep) ->
      (() ->
        switch dep
          when 'underscore' then root._
          else null
      )() or throw new Error "Unmet dependency #{dep}"
    (factory) -> root.myModule = factory(require)
) this

define (require) ->
  _ = require 'underscore'

Cleaning up JavaScript by returning

If you are like me, you are probably used to coding CoffeeScript blind-folded and you rarely look at the output code (unless something breaks, that is, and maybe not even then). Sometimes, though, code that works might not be as nice in JavaScript as it seems in CoffeeScript. In some cases this is because in CoffeeScript, everything is an expression, and the last expression evaluated will be returned from a function.

Consider this example:

b = []

a = (x) ->
  b.push i + 1 for i in x

This function returns the result of the list comprehension, because that is the last expression in the function. It compiles to this handful:

var a, b;

b = [];

a = function(x) {
  var i, _i, _len, _results;
  _results = [];
  for (_i = 0, _len = x.length; _i < _len; _i++) {
    i = x[_i];
    _results.push(b.push(i + 1));
  }
  return _results;
};
var a;

But clearly, in this case, we aren't really interested in the return value which generated JavaScript frantically gathers and returns. In this instance, we should simply return at the end of the function and let CoffeeScript know that we don't expect any meaningful return value.

a = (x) ->
  b.push i + 1 for i in x
  return

This makes the JavaScript a lot slimmer, and depending on what you are doing, much faster, too.

a = function(x) {
  var i, _i, _len;
  for (_i = 0, _len = x.length; _i < _len; _i++) {
    i = x[_i];
    b.push(i + 1);
  }
};

License

Creative
Commons License

CoffeeScript Tricks by Branko Vukelic is licensed under a Creative Commons Attribution 3.0 Unported License.
Based on a work at https://github.com/foxbunny/cstricks.