The Little Book on CoffeeScript

Classes

Classes in JavaScript seem to have the kind of effect that cloves of garlic have to Dracula for some purists; although, let's be honest, if you're that way inclined, you're unlikely to be reading a book on CoffeeScript. However, it turns out that classes are just as damn useful in JavaScript as they are in other languages and CoffeeScript provides a great abstraction.

Behind the scenes, CoffeeScript is using JavaScript's native prototype to create classes; adding a bit of syntactic sugar for static property inheritance and context persistence. As a developer all that's exposed to you is the class keyword.

class Animal

In the example above, Animal is the name of the class, and also the name of the resultant variable that you can use to create instances. Behind the scenes CoffeeScript is using constructor functions, which means you can instantiate classes using the new operator.

animal = new Animal

Defining constructors (functions that get invoked upon instantiation) is simple, just use a function named constructor. This is akin to using Ruby's initialize or Python's __init__.

class Animal
  constructor: (name) ->
    @name = name

In fact, CoffeeScript provides a shorthand for the common pattern of setting instance properties. By prefixing argument's with @, CoffeeScript will automatically set the arguments as instance properties in the constructor. Indeed, this shorthand will also work for normal functions outside classes. The example below is equivalent to the last example, where we set the instance properties manually.

class Animal
  constructor: (@name) ->

As you'd expect, any arguments passed on instantiation are proxied to the constructor function.

animal = new Animal("Parrot")
alert "Animal is a #{animal.name}"

Instance properties

Adding additional instance properties to a class is very straightforward, it's exactly the syntax as adding properties onto an object. Just make sure properties are indented correctly inside the class body.

class Animal
  price: 5

  sell: (customer) ->

animal = new Animal
animal.sell(new Customer)

Context changes are rife within JavaScript, and earlier in the Syntax chapter we talked about how CoffeeScript can lock the value of this to a particular context using a fat arrow function: =>. This ensures that whatever context a function is called under, it'll always execute inside the context it was created in. CoffeeScript has extended support for fat arrows to classes, so by using a fat arrow for an instance method you'll ensure that it's invoked in the correct context, and that this is always equal to the current instance.

class Animal
  price: 5

  sell: =>
    alert "Give me #{@price} shillings!"

animal = new Animal
$("#sell").click(animal.sell)

As demonstrated in the example above, this is especially useful in event callbacks. Normally the sell() function would be invoked in the context of the #sell element. However, by using fat arrows for sell(), we're ensuring the correct context is being maintained, and that this.price equals 5.

Static properties

How about defining class (i.e. static) properties? Well, it turns out that inside a class definition, this refers to the class object. In other words you can set class properties by setting them directly on this.

class Animal
  this.find = (name) ->      

Animal.find("Parrot")

In fact, as you may remember, CoffeeScript aliases this to @, which lets you write static properties even more succinctly:

class Animal
  @find: (name) ->

Animal.find("Parrot")

Inheritance & Super

It wouldn't be a proper class implementation without some form of inheritance, and CoffeeScript doesn't disappoint. You can inherit from another class by using the extends keyword. In the example below, Parrot extends from Animal, inheriting all of its instance properties, such as alive()

class Animal
  constructor: (@name) ->

  alive: ->
    false

class Parrot extends Animal
  constructor: ->
    super("Parrot")

  dead: ->
    not @alive()

You'll notice that in the example above, we're using the super() keyword. Behind the scenes, this is translated into a function call on the class' parent prototype, invoked in the current context. In this case, it'll be Parrot.__super__.constructor.call(this, "Parrot");. In practice, this will have exactly the same effect as invoking super in Ruby or Python, invoking the overridden inherited function.

Unless you override the constructor, by default CoffeeScript will invoke the parent's constructor when instances are created.

CoffeeScript uses prototypal inheritance to automatically inherit all of a class's instance properties. This ensures that classes are dynamic; even if you add properties to a parent class after a child has been created, the property will still be propagated to all of its inherited children.

class Animal
  constructor: (@name) ->

class Parrot extends Animal

Animal::rip = true

parrot = new Parrot("Macaw")
alert("This parrot is no more") if parrot.rip

It's worth pointing out though that static properties are copied to subclasses, rather than inherited using prototype as instance properties are. This is due to implementation details with JavaScript's prototypal architecture, and is a difficult problem to work around.

Mixins

Mixins are not something supported natively by CoffeeScript, for the good reason that they can be trivially implemented yourself. For example, here's two functions, extend() and include() that'll add class and instance properties respectively to a class.

extend = (obj, mixin) ->
  obj[name] = method for name, method of mixin        
  obj

include = (klass, mixin) ->
  extend klass.prototype, mixin

# Usage
include Parrot,
  isDeceased: true

(new Parrot).isDeceased

Mixins are a great pattern for sharing common logic between modules when inheritance is not suited. The advantage of mixins, is that you can include multiple ones, compared to inheritance where only one class can be inherited from.

Extending classes

Mixins are pretty neat, but they're not very object orientated. Instead, let's integrate mixins into CoffeeScript's classes. We're going to define a class called Module that we can inherit from for mixin support. Module will have two static functions, @extend() and @include() which we can use for extending the class with static and instance properties respectively.

moduleKeywords = ['extended', 'included']

class Module
  @extend: (obj) ->
    for key, value of obj when key not in moduleKeywords
      @[key] = value

    obj.extended?.apply(@)
    this

  @include: (obj) ->
    for key, value of obj when key not in moduleKeywords
      # Assign properties to the prototype
      @::[key] = value

    obj.included?.apply(@)
    this

The little dance around the moduleKeywords variable is to ensure we have callback support when mixins extend a class. Let's take a look at our Module class in action:

classProperties = 
  find: (id) ->
  create: (attrs) ->

instanceProperties =
  save: -> 

class User extends Module
  @extend classProperties
  @include instanceProperties

# Usage:
user = User.find(1)

user = new User
user.save()

As you can see, we've added some static properties, find() and create() to the User class, as well as some instance properties, save(). Since we've got callbacks whenever modules are extended, we can shortcut the process of applying both static and instance properties:

ORM = 
  find: (id) ->
  create: (attrs) ->
  extended: ->
    @include
      save: -> 

class User extends Module
  @extend ORM

Super simple and elegant!