CoffeeScript is a tiny language which compiles into JavaScript. Its expressive, yet terse syntax dramatically increases the readability of your code, which in turn makes it easier to maintain and less likely to contain bugs. In the words of its creator, Jeremy Ashkenas, CoffeeScript allows you to “write what you mean, instead of writing within the limits of historical accident”.
An additional benefit to writing CoffeeScript is that the JavaScript it compiles to will run in older versions of Internet Explorer. CoffeeScript also lets you forget about common JS pitfalls, such as trailing commas and automatic semicolon insertion.
And it’s gaining in popularity! Spurred on by its adoption in the Rails community (Rails 3.1+ ships with built-in CoffeeScript support), CoffeeScript recently entered the Tiobe index of the top 100 programming languages, where it was raked in 64th place. This was ahead of Dart (66th place) and TypeScript (not listed), both of which also compile to JavaScript.
So are you ready to give CoffeeScript a try? In this article I will demonstrate how to install it, as well as its basic concepts.
Installation
You can install CoffeeScript globally using the Node Package Manager (npm) by typing the following command in to your terminal:
npm install coffee-script -g
You should install it globally so you can later access it in terminal with the command coffee
.
If you need a primer on using npm, then please refer to this recently published SitePoint article.
Compilation
CoffeeScript files have the .coffee
file extension. These files are either manually compiled, or you set a watcher that will compile your script each time it is saved with different contents.
To compile manually, go to the directory where the script is:
cd E:\apps\something\logic
And run the following command:
coffee -c app.coffee
This will create an app.js
file in the same directory which you can then include in your project.
However, you most likely want app.js
to be refreshed each time you save the file. Therefore you compile it and add a watcher by typing:
coffee -cw app.coffee
Please note that in the latest version of CoffeeScript (1.9.1) there is a bug that causes the watcher not to work. All of the following examples were tested using CoffeeScript v 1.9.0.
CoffeeScript Basics
In CoffeeScript you do not have to declare variables as you do in JavaScript, although often you would need to set an initial value. We also do not have to type semi-colons ( ;
) at the end of a line.
This means that you write:
hasBody = true
instead of :
var hasBody = true;
You can also call functions without using parentheses, but that is desirable only for outermost function calls. Therefore, you can do the following:
$(".messages") .show 'slow'
instead of:
$(".messages").show('slow');
Indentation matters a lot in CoffeeScript. You should indent with two spaces or a tab:
if hasBody
alert "Hello Body"
else
alert "No Body"
Booleans and Conditionals
In CoffeeScript the keywords on
, yes
and true
are all equivalent and refer to the Boolean true
, whereas off
, no
and false
are also equivalent and refer to the Boolean false
.
You can use is
and isnt
to check for equality or lack of equality ( ===
and !==
) .
You can use then
to make single-line conditional statements.
You can use and
and or
to refer to &&
and ||
respectively.
All of which means that you can compare a value to two other values without repeating yourself.
An example of these concepts:
x = 53
y = 40
z = 33
b = true
if b is on and x is 53 and z < y > 11 then alert "ALRIGHT!"
The final statement compiles to:
if (b === true && x === 53 && (z < y && y > 11)) {
alert('ALRIGHT!');
}
Iteration, Filters and Ranges
The for .. in
syntax in CoffeeScript is used for iterating over an array, while a for .. of
loop is used for iterating over the properties of an object.
arr = [1, 2, 3]
for val in arr
console.log(val);
spartacus =
type: "cat"
legs: 4
fur: yes
for key, value of spartacus
console.log "#{key}: #{value}"
Notice the string interpolation in the final statement. This is achieved using a #{variableName}
syntax.
This will output:
1
2
3
type: cat
legs: 4
fur: true
You can combine this with CoffeeScript’s when
keyword to filter items in an array:
spartacus =
type: "cat"
legs: 4
fur: yes
shrimpy =
type: "fish"
legs: 0
fur: no
pets = [spartacus, shrimpy]
myPet = pet for pet in pets when pet.type is "cat"
console.log myPet
Outputs:
Object {type: "cat", legs: 4, fur: true}
Notice that you can specify the statement to be executed in the loop before you write the loop. This is useful when writing one-liner loops.
This could also be written as:
for pet in pets when pet.type is "cat"
myPet = pet
console.log myPet
An extremely useful feature of CoffeeScript is the ability to create numeric ranges. These can be inclusive and exclusive:
someValues = [0..10]
sameValues = [0...11]
When compiled to JavaScript the arrays look like this:
someValues = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
sameValues = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
If you create a range with more than 20 elements, CoffeScript gets a little clever, as you can see from the generated JavaScript. Notice that execution takes place in an anonymous function to ward against scope leakage and variable conflict.
var someValues, _i, _results;
someValues = (function() {
_results = [];
for (_i = 0; _i <= 21; _i++){ _results.push(_i); }
return _results;
}).apply(this);
Functions and the “this” Keyword
To create functions you use ->
followed by the definition. If you have to add parameters you add them inside parenthesis () before the ->
You can add default values by setting the parameter to equal something.
Examples of functions with CoffeeScript:
myCoffee = ->
console.log "C'est un cafe"
makeCoffee = (brand = "Hogwarts") ->
console.log "Making a coffee #{brand}-style"
myCoffee()
makeCoffee()
Logs to the console:
C'est un cafe
Making a coffee Hogwarts-style
You can use @
instead of the this
keyword:
$("a").click ->
$(@).hide 'slow'
This compiles to:
$("a").click(function() {
return $(this).hide('slow');
});
ES6 Style Classes and OOP
CoffeeScript facilitates object-oriented programming and inheritance as well. You can define classes like this:
class Animal
constructor: (@name, @breed) ->
@.introduce = ->
console.log "Hi, I am #{@.name}, one hell of a #{@.breed}"
husky = new Animal("Johnny", "Siberian Husky")
husky.introduce()
Outputs in console:
Hi, I am Johnny, one hell of a Siberian Husky
typing @
before the parameter’s name in the constructor, causes the parameter to be immediately set in the constructor. Alternatively, you could just write @.name = name
in the constructor function.
You can extend classes as well:
class Dog extends Animal
Dog.prototype.bark = ->
console.log "#{@name} barks!"
newHusky = new Dog("Lassy", "Labrador Husky")
newHusky.introduce()
newHusky.bark()
This code outputs:
Hi, I am Lassy, one hell of a Labrador Husky
Lassy barks!
Your functions can accept an unlimited number of arguments if you add ...
(ellipsis) after the parameter. In this case, all values after and including that parameter are added in the form of an array.
Here is how you could achieve that:
showAwards = (awards...) ->
console.log ("Awards collected : #{awards.join ', '}")
showAwards "Award 1", "Award 2", "Award 3"
The code above outputs:
Awards collected : Award 1, Award 2, Award 3
The final thing I want to mention here is that when you are inside any function CoffeeScript automatically returns the result from the last statement executed. Therefore, all your functions have an implicit return value (just as you saw in the anonymous function that handles anchor clicks above).
Conclusion
In this article I have demonstrated many of the features that make CoffeeScript such a pleasure to work with. I will build on this knowledge in a future article when I will use CoffeeScript to create the well-known game TicTacToe. Until then, let me know what you think: Are you using CoffeeScript already? Has this article made you curious to try it out? Or does CoffeeScript offer nothing more than an unnecessary layer of complexity?