Getting started
Introduction
CoffeeScript is a little language that compiles into JavaScript. It provides syntactic sugar inspired by Ruby, Python, and Haskell to enhance JavaScript's readability. CoffeeScript 2.7+ compiles to modern ES6+ JavaScript.
Note: Many CoffeeScript features inspired ES6 (arrow functions, classes, destructuring). Usage has declined as modern JavaScript adopted these features, but it's still maintained and used in legacy projects.
Installation
# Global installation
npm install -g coffeescript
# Project installation
npm install --save-dev coffeescript
# Run REPL
coffee
# Compile file
coffee -c script.coffee
# Watch and compile
coffee -cw src/
Quick Example
# CoffeeScript
square = (x) -> x * x
cube = (x) -> square(x) * x
list = [1, 2, 3, 4, 5]
squares = (square num for num in list)
Compiles to:
// JavaScript (ES6)
var cube, list, square, squares;
square = function (x) {
return x * x;
};
cube = function (x) {
return square(x) * x;
};
list = [1, 2, 3, 4, 5];
squares = (function () {
var i, len, results;
results = [];
for (i = 0, len = list.length; i < len; i++) {
num = list[i];
results.push(square(num));
}
return results;
})();
Syntax Basics
Variables
# Assignment
name = "Alice"
age = 30
# Multiple assignment
[first, second] = [1, 2]
# Object destructuring
{name, age} = person
# Swapping
[a, b] = [b, a]
Functions
# Arrow syntax
square = (x) -> x * x
# Multi-line
greet = (name) ->
message = "Hello, #{name}!"
console.log message
message
# No parameters
sayHi = -> console.log "Hi!"
# Default parameters
greet = (name = "World") ->
"Hello, #{name}!"
# Rest parameters
sum = (nums...) ->
nums.reduce ((a, b) -> a + b), 0
Conditionals
# If/else
if age > 18
"adult"
else
"minor"
# Unless (if not)
unless ready
prepare()
# Inline if
mood = if happy then "😊" else "😢"
# Postfix conditionals
alert "Ready!" if ready
console.log "Not ready" unless ready
# Existential operator
name ?= "Anonymous" # Set if undefined
Operators
Existential Operator
# Check existence (not null/undefined)
user?.name
# Safe method call
user?.getName?()
# Default value
name = user?.name ? "Guest"
# Existential assignment
speed ?= 100 # Set if null/undefined
Compiles to:
var name, ref, ref1;
if (user != null) {
user.name;
}
if (typeof user !== "undefined" && user !== null) {
if (typeof user.getName === "function") {
user.getName();
}
}
name = (ref = user != null ? user.name : void 0) != null ? ref : "Guest";
if (speed == null) {
speed = 100;
}
Comparison Operators
# Equality
a is b # a === b
a isnt b # a !== b
# Comparison
a > b
a < b
a >= b
a <= b
# Chained comparisons
10 < age < 100
Logical Operators
# And/or
if ready and willing
proceed()
if tired or hungry
rest()
# Not
if not ready
wait()
# Aliases
true, yes, on # All compile to true
false, no, off # All compile to false
Strings
String Interpolation
# Double quotes allow interpolation
name = "Alice"
greeting = "Hello, #{name}!"
# Expressions in interpolation
"The answer is #{2 + 2}"
# Single quotes are literal
literal = 'Hello, #{name}!' # Not interpolated
Multi-line Strings
# Block strings (preserve indentation)
html = """
<div>
<h1>Title</h1>
<p>Content</p>
</div>
"""
# Block strings remove leading indent
message = """
Hello,
World!
""" # "Hello,\nWorld!"
Heredocs
# Multi-line with interpolation
user = "Alice"
email = """
Dear #{user},
Welcome to our service!
Best regards,
Team
"""
Arrays & Objects
Arrays
# Array literals
numbers = [1, 2, 3, 4, 5]
# Multi-line arrays (no commas needed)
foods = [
'apple'
'banana'
'cherry'
]
# Ranges
numbers = [1..5] # [1, 2, 3, 4, 5]
numbers = [1...5] # [1, 2, 3, 4]
# Slicing
list[0..2] # First 3 items
list[2..] # From index 2 to end
list[..-2] # All but last item
Objects
# Object literals
person =
name: "Alice"
age: 30
greet: -> "Hi, I'm #{@name}"
# Computed properties
key = "name"
obj = {[key]: "value"}
# Shorthand (ES6 style)
name = "Alice"
age = 30
person = {name, age}
Destructuring
# Array destructuring
[first, second, rest...] = [1, 2, 3, 4, 5]
# Object destructuring
{name, age} = person
# Nested destructuring
{address: {city}} = person
# Default values
{name = "Guest"} = user
Comprehensions
List Comprehensions
# Map
squares = (x * x for x in [1..10])
# Filter
evens = (x for x in [1..10] when x % 2 is 0)
# Multiple conditions
results = (x for x in list when x > 10 and x < 20)
# Nested loops
pairs = ([x, y] for x in [1..3] for y in [1..3])
Compiles to:
var evens, i, j, k, l, pairs, results, squares, x, y;
squares = (function () {
var i, results;
results = [];
for (i = 1; i <= 10; i++) {
results.push(i * i);
}
return results;
})();
evens = (function () {
var i, results;
results = [];
for (i = 1; i <= 10; i++) {
if (i % 2 === 0) {
results.push(i);
}
}
return results;
})();
Object Comprehensions
# Create object from array
obj = {[key]: value for key, value in pairs}
# Transform object
doubled = {key: val * 2 for key, val of numbers}
Loop Comprehensions
# Iterating arrays
for item in array
console.log item
# Iterating objects
for key, value of object
console.log "#{key}: #{value}"
# With index
for item, index in array
console.log "#{index}: #{item}"
# Loop with condition
for item in array when item.active
process item
Classes
Class Definition
class Animal
constructor: (@name) ->
@alive = true
move: (meters) ->
console.log "#{@name} moved #{meters}m"
# Create instance
dog = new Animal("Rex")
dog.move(10)
Compiles to:
var Animal, dog;
Animal = class Animal {
constructor(name) {
this.name = name;
this.alive = true;
}
move(meters) {
return console.log(`${this.name} moved ${meters}m`);
}
};
dog = new Animal("Rex");
dog.move(10);
Inheritance
class Dog extends Animal
constructor: (name, @breed) ->
super(name)
bark: ->
console.log "Woof! I'm a #{@breed}"
move: ->
console.log "Running..."
super(5)
# Create instance
rex = new Dog("Rex", "Labrador")
rex.bark()
rex.move()
Static Methods
class MathUtils
@PI: 3.14159
@square: (x) ->
x * x
@cube: (x) ->
x * x * x
# Usage
MathUtils.square(5) # 25
MathUtils.PI # 3.14159
Getters and Setters
class Person
constructor: (@firstName, @lastName) ->
@getter 'fullName', ->
"#{@firstName} #{@lastName}"
@setter 'fullName', (name) ->
[@firstName, @lastName] = name.split(' ')
Control Flow
Switch/Case
# Switch statement
switch day
when "Mon", "Tue", "Wed", "Thu", "Fri"
console.log "Weekday"
when "Sat", "Sun"
console.log "Weekend"
else
console.log "Invalid day"
# Switch with expressions
result = switch grade
when "A" then "Excellent"
when "B" then "Good"
when "C" then "Average"
else "Poor"
Try/Catch
try
riskyOperation()
catch error
console.error "Error:", error
finally
cleanup()
# Inline try
value = try JSON.parse(str) catch e then {}
Loops
# While loop
while condition
doSomething()
# Until loop (while not)
until ready
wait()
# Loop (infinite)
loop
doSomething()
break if condition
# Break and continue
for item in list
continue if item.skip
break if item.stop
process item
Modern JavaScript Features
Async/Await
# Async function
fetchData = (url) ->
response = await fetch(url)
data = await response.json()
data
# Async arrow
processUser = (id) ->
user = await getUser(id)
posts = await getPosts(user)
{user, posts}
Spread Operator
# Array spread
numbers = [1, 2, 3]
more = [0, numbers..., 4, 5]
# Function arguments
Math.max items...
# Object spread
defaults = {a: 1, b: 2}
options = {defaults..., c: 3}
Destructuring with Defaults
# Function parameters
greet = ({name = "Guest", age = 0} = {}) ->
"Hello, #{name} (#{age})"
# Array with defaults
[a = 1, b = 2] = someArray
Template Literals
# Multi-line with interpolation
message = """
Hello #{name},
You have #{count} new messages.
"""
# Expression interpolation
result = "Sum: #{numbers.reduce(((a,b) -> a + b), 0)}"
Compilation
CLI Commands
# Compile single file
coffee -c script.coffee
# Compile to specific output
coffee -c -o dist/ src/script.coffee
# Watch mode
coffee -cw src/
# Join files
coffee -j output.js -c input1.coffee input2.coffee
# Compile and print
coffee -p script.coffee
# Print without running
coffee -e "console.log 'hi'"
# Bare mode (no function wrapper)
coffee -cb script.coffee
Compilation Options
# Source maps
coffee -cm script.coffee
# ES6 output format
coffee -c --transpile script.coffee
# Show tokens
coffee -t script.coffee
# Show AST
coffee --ast script.coffee
# Print version
coffee -v
Node.js Integration
// Register CoffeeScript in Node
require("coffeescript/register");
// Now you can require .coffee files
const myModule = require("./module.coffee");
Build Tools
// Webpack config
module.exports = {
module: {
rules: [
{
test: /\.coffee$/,
use: "coffee-loader",
},
],
},
resolve: {
extensions: [".js", ".coffee"],
},
};
Common Patterns
Module Pattern
# Export in Node.js
module.exports = class MyClass
constructor: ->
# ...
# Or export object
module.exports = {
method1: -> # ...
method2: -> # ...
}
IIFE (Immediately Invoked Function Expression)
# CoffeeScript auto-wraps in IIFE
do ->
privateVar = "secret"
window.publicAPI =
getData: -> privateVar
Callback Pattern
# Traditional callback
fs.readFile 'file.txt', (err, data) ->
return console.error err if err
console.log data
# Promise pattern
fetchData()
.then (data) -> process data
.catch (err) -> console.error err
Event Handlers
# jQuery style
$ ->
$('#button').on 'click', (e) ->
e.preventDefault()
console.log 'Clicked!'
# DOM event
element.addEventListener 'click', (e) ->
console.log e.target
JavaScript Comparison
Function Syntax
# CoffeeScript
square = (x) -> x * x
multiply = (a, b) -> a * b
// JavaScript ES6
const square = (x) => x * x;
const multiply = (a, b) => a * b;
Class Syntax
# CoffeeScript
class Person
constructor: (@name, @age) ->
greet: ->
"Hi, I'm #{@name}"
// JavaScript ES6
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
greet() {
return `Hi, I'm ${this.name}`;
}
}
Array Methods
# CoffeeScript
squares = (x * x for x in [1..5])
evens = (x for x in list when x % 2 is 0)
// JavaScript ES6
const squares = [1, 2, 3, 4, 5].map((x) => x * x);
const evens = list.filter((x) => x % 2 === 0);
Destructuring
# CoffeeScript
{name, age} = person
[first, rest...] = array
// JavaScript ES6
const { name, age } = person;
const [first, ...rest] = array;
Gotchas
Implicit Returns
# CoffeeScript returns last expression
getValue = ->
x = 10
x + 5 # Returns 15
# Explicit return
getValue = ->
x = 10
return x + 5
console.log "Never reached"
Every CoffeeScript function returns the last expression. Use explicit return or assign to variable to avoid unexpected returns.
Whitespace Sensitivity
# WRONG: Inconsistent indentation
if condition
doThis()
doThat() # Error!
# RIGHT: Consistent indentation
if condition
doThis()
doThat()
CoffeeScript uses significant whitespace. Always use consistent indentation (2 spaces recommended).
Existential Operator
# ?= only assigns if null/undefined
x = false
x ?= true # x is still false!
# Use || for falsy values
x = false
x = x || true # x is now true
The ?= operator checks for null/undefined, not falsy values like false, 0, or "".
Global Variables
# Variables are local by default
func = ->
x = 10 # Local to function
# Use global explicitly
func = ->
global.x = 10 # Or window.x in browser
CoffeeScript makes all variables local by default. Use global or window for globals.
this/@ Context
class Widget
constructor: ->
@element = $('#widget')
# WRONG: Loses context
@element.on 'click', @handleClick
# RIGHT: Fat arrow preserves context
@element.on 'click', (e) => @handleClick(e)
handleClick: (e) ->
console.log @ # 'this' context
Use fat arrow => to preserve this context in callbacks.
CoffeeScript vs Modern JavaScript
What CoffeeScript Pioneered
CoffeeScript influenced many ES6+ features:
| CoffeeScript (2009) | JavaScript (ES6+/2015+) |
|---|---|
-> arrow functions |
=> arrow functions |
class syntax |
class syntax |
[a..b] ranges |
No direct equivalent |
{a, b} destructuring |
{a, b} destructuring |
`string #{expr}` interpolation |
`string ${expr}` template literals |
for..in array iteration |
for..of array iteration |
a ?= b existential |
a ?? b nullish coalescing (ES2020) |
obj?.prop safe navigation |
obj?.prop optional chaining (ES2020) |
When to Use CoffeeScript
Use CoffeeScript if:
- Maintaining legacy CoffeeScript projects
- You prefer significant whitespace syntax
- Working with Ruby/Python developers who prefer similar syntax
- Need ranges
[1..10]feature
Use Modern JavaScript if:
- Starting new projects
- Working with modern frameworks (React, Vue, Angular)
- Need broader ecosystem support
- Want better IDE/tooling support
- Working in teams unfamiliar with CoffeeScript
Also see
- CoffeeScript Official Site - Official documentation
- CoffeeScript GitHub - Source code and issues
- CoffeeScript Cookbook - Common patterns and recipes
- Try CoffeeScript - Online compiler and REPL
- ES6 Features - Modern JavaScript features inspired by CoffeeScript