Classes in CoffeeScript

JavaScript doesn’t have a traditional class system. Instead, it has prototypes. Prototypes can be extremely versatile and powerful, but they’re confusing to novices. Hence, CoffeeScript has created a traditional class system. But… how? CoffeeScript’s catchphrase is “It’s Just JavaScript,” and JavaScript is distinctly missing a traditional class system. In this article, we go over the basics of creating a CoffeeScript class. In the next article, we dig into the (relatively advanced) generated JavaScript to figure out how the magic works.

The Parent Class

We’ll do this mostly by example, since it should be fairly easy for those who have read my introductory article on coffeescript to pick up on what’s happening.

class Bourgeoisie
  constructor: (@age, @privilegeConstant) ->

  worry: ->
    console.log("My stocks are down 1%!")

  profit: (hardWork, luck) ->
    return (@age - 23) * hardWork * (luck + @privilegeConstant)

elite = new Bourgeoisie(29, 397)
elite.worry() # "My stocks are down 1%!"
elite.profit(20, 50) #53640

We declare a class called Bourgeoisie. Functions on a class are declared as follows:

functionName: (arguments) ->
  code

The constructor function is named, clearly enough, constructor. It takes two arguments, age and priviligeConstant, and automatically assigns them as instance variables (@ is the CoffeeScript replacement for this, and when used in the argument of a constructor automatically assigns the variable to the instance). The constructor is called automatically when you create a new Bourgeoisie, like at the bottom of the code sample. We also have two other functions. The first, worry, takes no arguments. The second, profit, takes two arguments and returns a number.

The Inherited Class

Now we want to have a class that inherits from Bourgeoisie. We’ll call it Senator.

class Senator extends Bourgeoisie
  worry: ->
    console.log("The polls are down 1%!")

senator = new Senator(45, 992)
senator.worry() # "The polls are down 1%!")
senator.profit(6, 10) # 132264

This class extends Bourgeoisie, which means that it has all the characteristics of the parent class. The constructor and profit functions are exactly the same, the only difference is that you make a call to Senator instead of Bourgeoisie when constructing an instance.

The worry function, on the other hand, is different. The Senator worries about polls more than stocks, so his worry overwrites that of the parent class. This overwriting is seen again in a Student class, shown below.

class Student extends Bourgeoisie
  worry: ->
    console.log("Does my privilege inherently make me complicit in the repression of less fortunate classes?")

  profit: (hardWork, luck, tuition) ->
    super(hardWork, luck) - tuition

student = new Student(21, 89)
student.worry() #"Does my privilege inherently make me complicit in the repression of less fortunate classes?"
student.profit(10, 10, 10000) #-11980

The student’s worry overwrites the parent’s worry (in an even more dramatic fashion than the Senator’s), and their profit is also overwritten. However, the overwrite is now dependent on the parent class’s profit function. It takes that and subtracts the tuition. It’s a bad time to be a student! But what you should really learn from this is the super keyword, which calls the parent’s version of a function.

But, I Liked Prototypes

Good for you! CoffeeScript gives you convenience, but it still leaves you with power. Let’s use it! Here’s our abbreviated example from last time:

object = (o) ->
    F = ->
    F.prototype = o
    new F()

soldier = new Object()
soldier.a = jump
soldier.r = machineGun

sniper = object(soldier)
sniper.r = snipe

woundedSniper = object(sniper)
woundedSniper.a = -> console.log('aaaargh my leg!')

woundedSoldier = object(soldier)
woundedSoldier.a = woundedSniper.a

This should seem familiar, because 90% of the change was replacing a few function‘s with pointy arrows. The prototypical inheritance system is untouched because, remember, CoffeeScript is just JavaScript. The syntax is cleaner, and there is nothing more to learn if you want to implement prototypes in the Brendan Eich style we used last time. That’s not to say that CoffeeScript doesn’t apply some shortcuts. You can use :: instead of prototype. However, in the Brendan Eich style of prototypes, we only need to use that once, in the object(o) method. We can also access the extends and super keywords, but those are used only in constructors – which we have once again hidden away in the object(o) method.

Conclusion

The classical inheritance system in CoffeeScript provides convenience and comfort to the average developer. In addition, the cleaner syntax of CoffeeScript makes implementing a true prototypal system slightly easier. There’s simply no way to lose.

Free book: Jump Start HTML5 Basics

Grab a free copy of one our latest ebooks! Packed with hints and tips on HTML5's most powerful new features.

  • Matt B

    Nice article, very concise and useful. Coffeescript works great for me. And thanks very much for using unexpected examples in your code–the standard “employee/address” and the like is quite stale.

  • Charles @ CodeConquest.com

    Thanks for this Jeffrey. I guess because JavaScript was the first programming language I really learnt, I’ve never considered the prototype system to be unusual or confusing. But I guess it could be confusing for those who are already familiar with another language.

    Visit http://CodeConquest.com for more helpful content on learning JavaScript.

  • Jeffrey Biles

    Thanks for the nice comments!

    Matt: I love to shake things up and I’m glad you enjoyed it as well!

    Charles: I’ve found the background of a learner to be a hugely important in how to best present them with information. Like you said, if javascript is the first language learned then prototypes don’t seem weird. They’re difficulty, but no difficulty than all the other stuff that they’re learning. If someone is coming from another language, they’ll easily pick up everything about javascript besides prototypes. That, coupled with a relative scarcity of good tutorials about the subject, can lead them to label prototypes as ‘hard’.