CoffeeScript has become increasingly popular over the last couple of years due to its adoption by the Node.js and Rails communities. Let’s take a look why it’s become the go-to language when people are wanting to write JavaScript in their apps.
Contents
What is CoffeeScript?
CoffeeScript is a language that gets compiled to JavaScript. Code in .coffee files are not interpreted at run time, like JavaScript, but are compiled into .js files.
Whether you’re writing for JavaScript for the Node.js or any given browser’s implementation, CoffeeScript can be written for any and all flavors of JavaScript.
Why CoffeeScript?
In the past, JavaScript hasn’t had the most stellar of reputations due to the copy-and-paste mentality of the 90’s and early 2000’s, it seemed that only a few had any idea what was going on. It was looked down on as a not so “serious” language. However, JavaScript as a language has a lot to offer and has some super features.
CoffeeScript makes use of the powerful features of JavaScript without learning the less known features of JavaScript by adding “syntactic sugar” to JavaScript. CoffeeScript doesn’t have the semi-colons and curly braces, similar syntax to the likes of Python and Ruby.
This means you can write less code and do things faster. It also makes it easier to read and maintain.
Due to optimizations CoffeeScript uses, when it’s compiled down, the resulting JavaScript is just as performant or in some instances it’s even more performant over hand-written code.
You can learn a lot about JavaScript by looking at the JavaScript that CoffeeScript compiles down to. If there’s something new that you see there, research it and see why CoffeScript compiles that way rather than what you’re used to seeing.
Installing CoffeeScript
Let’s install CoffeeScript so you can follow along. CoffeeScript is a Node.js package. So it depends on Node.js and the package manager (npm).
On OS X
Node.js can be installed using Homebrew, an open source Package Manager for OS X. You can review the installation process of Homebrew here.
Make sure you’re Homebrew is up to date by typing brew update
in your terminal.
The Homebrew formula for Node.js is node
. So to install Node.js you type in brew install node
into your terminal. Follow any additional instructions that appear if you need to alter PATH
s or anything like that.
Then type npm install -g coffee-script
to install it globally.
On Windows
Visit Node.js’ website and press the “Install” button. It should auto-detect your flavor of Windows you’re using and an installer will start to download.
Go through the install process.
Once it’s installed open up your Command Prompt and type npm install -g coffee-script
to install it globally.
And finally
Type coffee -v
in your command line to see it’s been installed correctly.
Command Line Usage
The coffee
compiler can be used in several ways. Here’s a couple to see what’s available.
Run and Compile
The following command will compile all your .coffee
files in a folder called coffeescripts
to .js
files in a parallel tree structure in the javascripts
folder.
coffee -o javascripts/ -c coffeescripts/
Warning
The option -c
means to compile and -o
means the output folder. Note the ordering is output then compile. This is because if you switch the order it doesn’t work!
Watcher
Running the above command every time you want to compile CoffeeScript files so there’s another handy option to use, -w
.
coffee -w -o javascripts/ -c coffeescripts/
The watcher listens to changes to the files in the coffeescripts
folder and compiles them on the fly.
If you add a new file to the coffeescripts
folder should compile, however if you add a new folder and a CoffeeScript file inside that it won’t be compiled. This could change in later versions.
Joining Files
You can also compile all your .coffee
files down to a single JavaScript file. This will reduce the number of HTTP request a browser has to make and improve performance. To do this use the -j
option like so:
coffee -j javascripts/app.js -c coffeescripts/*.coffee
The *
is a wildcard operator.
REPL
If you type coffee
without any options an interactive shell opens. This is similar to irb
in Ruby. This is a great tool for things out in the console and performing quick experiments.
Text Editor Highlighting
If you’re going to code in CoffeeScript the best thing to do is have syntax highlighting.
TextMate
Jeremy Ashkenas, CoffeeScript’s inventor, created a handy TextMate bundle. For installation instructions go here.
Sublime Text 2
Sublime Text 2 has a plugin created by GitHub user Xavura. You can install this via the Sublime Package Control, if you have that already.
The Good Stuff a.k.a. The Syntax
Variables
Variables in CoffeeScript don’t require the keyword var
. Simply name your variable, an equals sign, and then the value.
year = 1985
speed = 88
Strings
Strings are declared with quotes like in most other languages.
first_name = "Marty"
To join or concatenate strings, you can list them one after another with a plus symbol in between, like this:
full_name = first_name + " McFly"
You can also include the hash symbol (#
), followed by curly braces, and within those the string variable.
full_name = "#{first_name} McFly"
The above two examples would create the string Marty McFly
.
The latter type of string interpolation only works when you use double quotes. If you use single-quotes the text is displayed “as is”. Single quote strings are literal strings.
full_name = '#{first_name} McFly'
So the above will be the string of #{first_name} McFly
. This mirrors behavior.
Functions
Functions are declared by naming the function, equals sign, and then a special function symbol (->
).
initialize_time_circuits = -> year * flux_capacitor_constant * speed
We haven’t used the keyword return
because every function in CoffeeScript returns the result of the last line of code.
Multi-lined functions are writen on multiple lines with each line after the declaration having some white space before it.
initialize_time_circuits = ->
year = 1885
year * flux_capacitor_constant * speed
This would set the year to 1885
every time. If we want the year to be a lot more flexible but have a default we can bring it up into the method declaraion like this:
initialize_time_circuits = (speed, year = 1885) ->
year * flux_capacitor_constant * speed
This will mean we can then call this with one value of the speed
the year
will always default to 1885
.
initialize_time_circuits(88)
You can also call methods without using parentheses. The above can also be written like:
initialize_time_circuits 88
Arrays
There are several different ways to initialize and declare arrays in CoffeeScript. You’re probably used to seeing arrays declared with square brackets and values separated by commas.
mcflys = ["Marty", "George", "Lorraine", "Dave", "Linda"]
You can also declare arrays over multiple lines like this:
mcflys = [
"Marty",
"George",
"Lorraine",
"Dave",
"Linda"
]
You have to have white space on each line.
Optionally, you can remove the trailing comma from each line.
mcflys = [
"Marty"
"George"
"Lorraine"
"Dave"
"Linda"
]
You can also use a combination of these two declaration styles.
The following example…
woah = [
0, 1, 9
6, 5, 4
8, 0, 0
]
…is the equivalent to:
woah = [0, 1, 9, 6, 5, 4, 8, 0, 0]
Which may be more or less legible depending on your application. CoffeeScript allows you to do it in a variety of ways to suit your needs.
Objects
Object literals or hashes can be declared like their JavaScript counterparts and in some other funky ways.
Given the following JSON…
teachers = {
teacher: {
name: "Andrew Chalkley",
hair: "Ginger"
},
teacher: {
name:"Jim Hoskins",
hair: "Ginger"
},
teacher: {
name:"Pasan Premaratne",
hair: "Black"
}
}
…you can drop the commas and curly braces:
teachers =
teacher:
name: "Andrew Chalkley"
hair: "Ginger"
teacher:
name: "Jim Hoskins"
hair: "Ginger"
teacher:
name: "Pasan Premaratne"
hair: "Black"
As long as you indent with some white space, the structure gets nested as you’d expect.
Ranges
Ranges are declared by two integer separated by two periods, wrapped in square brackets.
days = [1..7]
Ranges aren’t a standard object type in Javascript so they get compiled down to arrays.
To get a sub-section of an array you can pass in a range too. Want to get days from tuesday_to_friday
where Monday is 1
and Sunday is 7
? Since arrays are indexed zero-based, we’d start at 1 and end at 4.
days = [1..7]
tuesday_to_friday = days[1..4]
You can replace whole sections of arrays in a similar way too.
The following…
days[1..4] = ["Tuesday", "Wednesday","Thursday", "Friday"]
Modifies the days
array to be [ 1, 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 6, 7 ]
.
You can use ranges to countdown too for example [10..0]
would be 10
to 0
.
Splat
A splat is a convenient way to accept multiple values as arguments to methods.
In the following example we want to award kudos points to a group of forum users for the number of posts in a given week.
The first person gets 10 points, the second gets 6, the third gets 2 and every one else will get a single point.
Let’s create a function, giveKudos
With first
, second
, and third
as parameters. For the rest well use a splat, which is the name of the variable followed by 3 periods. Within the giveKudos
method let’s call an unimplemented addKudosToUser
which takes two parameters, the user and the points to be added. We’re not going to worry about the implementation it’s just for illustrative purposes.
giveKudos = (first, second, third, rest...) ->
addKudosToUser first, 10
addKudosToUser second, 6
addKudosToUser third, 2
Next we can use a for in
to loop over the rest
.
giveKudos = (first, second, third, rest...) ->
addKudosToUser first, 10
addKudosToUser second, 6
addKudosToUser third, 2
for user in rest
addKudosToUser user, 1
Given we have an array of users:
users = ["Pasan", "Amit", "Jim", "Andrew", "Allison", "Jason", "A.J."]
We want Pasan to get 10 points, Amit to get 6, Jim to get 2 and then the rest to get 1.
We need to pass in users with the periods in to the method to properly assign the array values to the parameters like this:
giveKudos users...
Without these periods, users
get’s assigned to the first
parameter and undefined
to the rest.
Classes
Classes can be declared trivially in CoffeeScript which is a refreshing contrast to the much more verbose JavaScript.
First write the keyword class
, followed by the name of your class.
Methods, including the constructor method, are written with the name of the method followed by a colon. White space is needed before each method name. You can then use the function operator (->
).
class TimeMachine
constructor: ->
To instantiate an instance of an object, use the keyword new
followed by the class name.
time_machine = new TimeMachine
Instance Variables
Instance variables start with an @
symbol. So we can have a pilot
class TimeMachine
constructor: (pilot) ->
@pilot = pilot
You can access them via dot notation too.
class TimeMachine
constructor: (pilot) ->
@pilot = pilot
time_machine = new TimeMachine "H. G. Wells"
console.log time_machine.pilot
In the above example the string H. G. Wells
would be printed to the console.
Instead of setting @pilot = pilot
you can write it in shorthand like this:
class TimeMachine
constructor: (@pilot) ->
Class Inheritance
To write a subclass you can use extends
like this:
class Tardis extends TimeMachine
class DeLorean extends TimeMachine
Let’s say TimeMachine
has a method go
with one parameter that gets logged out when the Time Machine is about to travel in time.
class TimeMachine
constructor: (@pilot) ->
go: (noise) ->
console.log noise
Now let’s for each time machine implement go and call the super
method.
class Tardis extends TimeMachine
go: ->
super "vorp vorp"
class DeLorean extends TimeMachine
go: ->
super "One point twenty-one gigawatts!"
doctors_wife = new Tardis "The Doctor"
doc_browns_wheels = new DeLorean "Marty"
doctors_wife.go()
doc_browns_wheels.go()
So calling go()
on instances of both time machines will print out it’s own respective sound.
General Syntax
With CoffeeScript you’ll find your code looking more like a sentence in English:
light_bulbs() if switch is on
Which is the same as:
light_bulbs() unless switch is not on
While the latter is harder to parse than the former, it does show you the scope of the new sugar.
Here’s a table of equivalents from the CoffeeScript documentation page on Operators and Aliases.
CoffeeScript | JavaScript |
---|---|
is | === |
isnt | !== |
not | ! |
and | && |
or | || |
true, yes, on | true |
false, no, off | false |
@, this | this |
of | in |
in | no JS equivalent |
Existential Operator
CoffeeScript brings another cool operator to the table called the Existential Operator, which is a question mark (?
).
Let’s say if a user
hasn’t signed in and been set yet. You want the login function to be called if the user
isn’t present, then just use the Existential Operator like so:
login() if not user?
You can also set default values like so:
year ?= 1885
You can use the Existential Operator in a similar way to a ternary operator. The following example would set greeting to be either message
, if not undefined
or the string `”Hello, World!”.
greeting = message ? "Hello, World!"
Finally there’s an accessor variant which you can use absorb up null
references when you’re chaining properties together.
ip = securitySystem.lastUser?().remoteSystem?.ipAddress
So instead of raising a TypeError
this would return undefined
.
Comprehensions
Comprehensions can make your code more readable. In our kudos example we had the for
loop going over two lines, when we could have written it on one line like this:
giveKudos = (first, second, third, rest...) ->
addKudosToUser first, 10
addKudosToUser second, 6
addKudosToUser third, 2
addKudosToUser user, 1 for user in rest
Another cool feature of looping over things in CoffeeScript is that the loop returns an array of the results of that loop. Let’s say I want to count from 0 to 100 in multiples of 10. All I do is prepend my loop with a variable assignment like so:
multiples = for num in [0..10]
num * 10
Adding parentheses you can bring it on to one line too!
multiples = (num * 10 for num in [0..10])
Conclusion
Woah! That was a lot to take in but as you can see CoffeeScript is full of tasty sugary syntax. Why not give it a bash in your next JavaScript project and see what it compiles down to under the hood?