Introduction
I love JavaScript, but let's be honest – the syntax can be clunky. Verbose function declarations, confusing this binding, lack of classes, awkward loops. JavaScript is powerful, but it could be prettier.
CoffeeScript addresses this. Created by Jeremy Ashkenas (of Backbone.js and Underscore.js fame), CoffeeScript is a language that compiles to JavaScript. It keeps JavaScript's good parts while fixing the annoying bits.
The result? Code that's shorter, clearer, and more expressive. Let me show you why CoffeeScript is worth learning.
What is CoffeeScript?
CoffeeScript is a programming language that compiles to JavaScript. You write CoffeeScript, it becomes JavaScript, which runs in browsers or Node.js.
Key features:
- Clean, Ruby/Python-inspired syntax
- No curly braces or semicolons
- Significant whitespace
- Classes and inheritance
- Comprehensions and destructuring
- Everything is an expression
It's not a framework or library – it's a different way to write JavaScript.
Installing CoffeeScript
CoffeeScript requires Node.js:
npm install -g coffee-script
Verify installation:
coffee --version
You now have the coffee command.
Your First CoffeeScript
CoffeeScript:
console.log "Hello, CoffeeScript!"
Compiles to JavaScript:
console.log("Hello, CoffeeScript!");
Run it:
coffee hello.coffee
Or compile to JavaScript:
coffee --compile hello.coffee
Creates hello.js.
The Basics
No Curly Braces
CoffeeScript uses indentation:
CoffeeScript:
if happy
console.log "I'm happy!"
else
console.log "I'm sad"
JavaScript:
if (happy) {
console.log("I'm happy!");
} else {
console.log("I'm sad");
}
Cleaner, less noise.
No Semicolons
CoffeeScript doesn't need semicolons:
name = "John"
age = 30
console.log name, age
Fewer keystrokes, cleaner code.
No var Keyword
Variables are automatically scoped:
CoffeeScript:
outer = "outside"
someFunction = ->
inner = "inside"
console.log outer, inner
JavaScript:
var outer = "outside";
var someFunction = function() {
var inner = "inside";
console.log(outer, inner);
};
CoffeeScript adds var automatically, preventing global variable pollution.
Functions
Much cleaner syntax:
CoffeeScript:
square = (x) -> x * x
add = (a, b) -> a + b
greet = (name) ->
console.log "Hello, #{name}!"
JavaScript:
var square = function(x) {
return x * x;
};
var add = function(a, b) {
return a + b;
};
var greet = function(name) {
console.log("Hello, " + name + "!");
};
The arrow -> defines functions. Implicit returns make single-line functions beautiful.
Default Parameters
greet = (name = "World") ->
console.log "Hello, #{name}!"
greet() # Hello, World!
greet("John") # Hello, John!
Splats
Collect remaining arguments:
sum = (numbers...) ->
total = 0
total += n for n in numbers
total
console.log sum(1, 2, 3, 4, 5) # 15
String Interpolation
Use #{} like Ruby:
name = "John"
age = 30
message = "My name is #{name} and I am #{age} years old"
Much better than JavaScript string concatenation.
Classes
CoffeeScript has real classes:
class Animal
constructor: (@name) ->
move: (meters) ->
console.log "#{@name} moved #{meters}m"
class Snake extends Animal
move: ->
console.log "Slithering..."
super 5
class Horse extends Animal
move: ->
console.log "Galloping..."
super 45
snake = new Snake("Sammy")
horse = new Horse("Tommy")
snake.move()
horse.move()
JavaScript equivalent is verbose:
var Animal = function(name) {
this.name = name;
};
Animal.prototype.move = function(meters) {
console.log(this.name + " moved " + meters + "m");
};
// ... much more code
CoffeeScript classes are syntactic sugar, but beautiful sugar.
@ Symbol
@ is shorthand for this:
class Person
constructor: (@name, @age) ->
greet: ->
console.log "Hi, I'm #{@name}"
Less typing, clearer code.
Arrays and Loops
Ranges
numbers = [1..10] # [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
letters = ['a'..'e'] # ['a', 'b', 'c', 'd', 'e']
countdown = [10..1] # [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
Comprehensions
Beautiful way to iterate:
# Iterate array
for name in ['Alice', 'Bob', 'Charlie']
console.log "Hello, #{name}!"
# With index
for name, index in ['Alice', 'Bob', 'Charlie']
console.log "#{index + 1}. #{name}"
# Create new array
squares = (x * x for x in [1..10])
# Iterate object
ages = {alice: 30, bob: 35, charlie: 40}
for name, age of ages
console.log "#{name} is #{age} years old"
Filters
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
evens = (n for n in numbers when n % 2 is 0)
# [2, 4, 6, 8, 10]
Operators
is vs ==
if name is "John"
console.log "Hi John"
if age isnt 30
console.log "Not 30"
Compiles to === and !== (the good equality operators).
Existence Operator
Check if variable exists and isn't null:
name = "John"
console.log name? # true
console.log missing? # false
# Set default only if doesn't exist
name ?= "Default"
Chained Comparisons
age = 25
if 18 <= age <= 65
console.log "Working age"
Much nicer than age >= 18 && age <= 65.
Everything is an Expression
In CoffeeScript, everything returns a value:
grade = if score >= 90
'A'
else if score >= 80
'B'
else if score >= 70
'C'
else
'F'
Even try/catch:
result = try
doSomethingRisky()
catch error
console.log "Error:", error
null
Destructuring
Extract values from arrays and objects:
# Array destructuring
[first, second, rest...] = [1, 2, 3, 4, 5]
# Object destructuring
{name, age} = {name: "John", age: 30}
# Function parameters
greet = ({name, age}) ->
console.log "#{name} is #{age} years old"
greet(name: "John", age: 30)
Practical Example
Let's build a simple to-do list:
class TodoList
constructor: ->
@tasks = []
add: (task) ->
@tasks.push task
console.log "Added: #{task}"
remove: (task) ->
index = @tasks.indexOf task
if index isnt -1
@tasks.splice index, 1
console.log "Removed: #{task}"
else
console.log "Task not found"
list: ->
if @tasks.length is 0
console.log "No tasks"
else
console.log "Tasks:"
console.log " #{index + 1}. #{task}" for task, index in @tasks
complete: (index) ->
if 0 <= index < @tasks.length
task = @tasks.splice(index, 1)[0]
console.log "Completed: #{task}"
else
console.log "Invalid index"
# Usage
todos = new TodoList()
todos.add "Write blog post"
todos.add "Review code"
todos.add "Deploy app"
todos.list()
todos.complete(0)
todos.list()
Clean, readable, maintainable.
Using in the Browser
Compile to JavaScript:
coffee --compile script.coffee
Include in HTML:
<script src="script.js"></script>
Or compile on-the-fly (development only):
<script src="coffee-script.js"></script>
<script type="text/coffeescript" src="script.coffee"></script>
For production, always precompile.
CoffeeScript with Node.js
Node supports CoffeeScript directly:
coffee server.coffee
Or require in JavaScript:
require('coffee-script');
var myModule = require('./module.coffee');
Many Node apps are written in CoffeeScript.
CoffeeScript with Rails
Rails 3.1 (coming soon) will include CoffeeScript support by default.
app/assets/javascripts/application.coffee:
$ ->
console.log "Page loaded!"
$('#button').click ->
alert "Button clicked!"
The asset pipeline compiles CoffeeScript automatically.
Common Patterns
jQuery in CoffeeScript
$ = jQuery
$ ->
$('.button').click (e) ->
e.preventDefault()
$(this).toggleClass 'active'
$('form').submit (e) ->
e.preventDefault()
name = $('#name').val()
email = $('#email').val()
console.log "Name: #{name}, Email: #{email}"
Much cleaner than JavaScript.
Module Pattern
App = do ->
privateVar = "secret"
privateMethod = ->
console.log privateVar
{
publicMethod: ->
privateMethod()
console.log "Public method"
}
App.publicMethod()
Debugging
CoffeeScript generates readable JavaScript:
CoffeeScript:
square = (x) -> x * x
console.log square 5
Generated JavaScript:
var square;
square = function(x) {
return x * x;
};
console.log(square(5));
You can debug the JavaScript like any other JS code.
Source Maps
CoffeeScript supports source maps:
coffee --compile --map script.coffee
Browser shows CoffeeScript line numbers in errors.
Pros and Cons
Advantages:
- Cleaner, more readable syntax
- Fewer lines of code
- Classes and inheritance
- Prevents common JavaScript mistakes
- More expressive
Disadvantages:
- Extra compilation step
- Learning curve
- Debugging can be tricky
- Not all JavaScript features are prettier in CoffeeScript
- Team needs to know CoffeeScript
Should You Use CoffeeScript?
Use CoffeeScript if:
- You find JavaScript syntax annoying
- You like Ruby/Python syntax
- You're building a new project
- Your team is on board
Stick with JavaScript if:
- You're comfortable with JavaScript
- You're maintaining existing code
- Your team doesn't want to learn CoffeeScript
- Extra compilation is a dealbreaker
CoffeeScript isn't for everyone, but many developers find it makes JavaScript development more enjoyable.
Resources
Official site: coffeescript.org – Try it in the browser
Documentation: Excellent, with lots of examples
The Little Book on CoffeeScript: Great introduction
CoffeeScript Cookbook: Common patterns and recipes
Source code: Many open-source projects use CoffeeScript
Next Steps
Ready to try CoffeeScript? Here's how to start:
1. Install CoffeeScript:
npm install -g coffee-script
2. Try the REPL:
coffee
Experiment with syntax interactively.
3. Convert simple JavaScript:
Take existing JavaScript, rewrite in CoffeeScript. See the difference.
4. Build something small:
A jQuery plugin, a Node script, anything. Experience the workflow.
5. Read generated JavaScript:
Understand what CoffeeScript produces.
CoffeeScript is JavaScript with better syntax. You're not learning a new language – you're learning a better way to write the language you know.
Give it a try. You might find yourself writing all your JavaScript in CoffeeScript.