CodeProject
CoffeeScript is a little language that compiles into JavaScript. The code compiles one-to-one into the equivalent JS, and there is no interpretation at runtime. You can use any existing JavaScript library seamlessly (and vice-versa).
The syntax of CoffeeScript is inspired by Ruby and Python, and implements many features from those two languages. This introduction references The Little Book on CoffeeScript and the official site of CoffeeScript heavily.
You are required to be familiar with JavaScript’s internals and idioms, and Ruby’s and/or Python’s syntax.
Overview
Some CoffeeScript codes are listed below for a quick overview. If you are familiar with JavaScript and Ruby, it will be very intuitive.
# Assignment:
number = 42
opposite = true
# Conditions:
number = -42 if opposite
# Functions:
square = (x) -> x * x
# Arrays:
list = [1, 2, 3, 4, 5]
# Objects:
math =
root: Math.sqrt
square: square
cube: (x) -> x * square x
# Splats:
race = (winner, runners...) ->
print winner, runners
# Existence:
alert "I knew it!" if elvis?
# Array comprehensions:
cubes = (math.cube num for num in list)
Syntax
Comments come first:
# A single-line comment
###
A multiline comment, perhaps a LICENSE.
###
CoffeeScript simply removes global variables. Behind the scenes, CoffeeScript wraps up scripts with anonymous function, keeping the local context, and automatically prefixes all variables assignments with var
. For example, take this simple variable assignment in CoffeeScript:
myVariable = "test"
The compiled JavaScript will be:
var myVariable;
myVariable = "test";
However, sometimes it’s useful to create global variables. You can do with the following pattern:
exports = this
exports.MyVariable = "foo-bar"
In the root context, this
is equal to the global object, and by creating a local exports
variable you’re making it really obvious to anyone reading your code exactly which global variables a script is creating.
CoffeeScript removes the rather verbose function
statement, and replaces it with a thin arrow: –>
. Functions can be one liners, or indented on multiple lines. The last expression is implicitly returned. The following two code snippets are equivalent to the third JavaScript code snippet:
func = -> "bar"
func = ->
# An extra line
"bar"
var func;
func = function() {
return "bar";
};
You can also specify arguments in a pair of parenthesis before the arrow.
times = (a, b) -> a * b
CoffeeScript supports default arguments too, for example:
times = (a = 1, b = 2) -> a * b
It will be compiled to:
var times;
times = function(a, b) {
if (a == null) {
a = 1;
}
if (b == null) {
b = 2;
}
return a * b;
};
You can also use splats to accept multiple arguments, denoted by …
sum = (nums...) ->
result = 0
nums.forEach (n) -> result += n
result
Functions can be invoked exactly as in JavaScript. However, like Ruby, CoffeeScript will automatically call functions if they are invoked with at least one argument.
a = "Howdy!"
alert a
# Equivalent to:
alert(a)
alert inspect a
# Equivalent to:
alert(inspect(a))
# and recommended to be:
alert inspect(a)
Object literals can be specified exactly as in JavaScript. However, CoffeeScript makes it easier.
object1 = {one: 1, two: 2}
# Without braces
object2 = one: 1, two: 2
# Using new lines instead of commas
object3 =
one: 1
two: 2
User.create(name: "John Smith")
Likewise, arrays can use whitespace instead of comma separators, although the square brackets ([]
) are still required.
array1 = [1, 2, 3]
array2 = [
1
2
3
]
array3 = [1,2,3,]
CoffeeScript has also stripped the trailing commas in array3
, another common source of cross-browser errors.
Flow control looks like Ruby, which doesn’t require too much explanation.
if true == true
"We're ok"
if true != true then "Panic"
if 1 > 0 then "Ok" else "Y2K!"
alert "It's cold!" if heat < 5
if not true then "Panic"
unless true
"Panic"
if true is 1
"type coercion fail"
if 10 == "+10" then "type coercion fail"
Note that CoffeeScript is converting ==
operators into === and !=
into !==
, since it’s recommended to always use the strict equality operator, and explicitly convert types if need be.
CoffeeScript brings Ruby style string interpolation to JavaScript. Multiline strings are also allowed, without having to prefix each line with a +
.
favorite_color = "Blue. No, yel..."
question = "Bridgekeeper: What.. is your favorite color?
Galahad: #{favorite_color}
Bridgekeeper: Wrong!
"
Array iteration in JavaScript has a rather archaic syntax. CoffeeScript comes to the rescue, with a beautiful syntax:
for name in ["Roger", "Roderick", "Brian"]
alert "Release #{name}"
for name, i in ["Roger the pickpocket", "Roderick the robber"]
alert "#{i} - Release #{name}"
# The postfix form:
release prisoner for prisoner in ["Roger", "Roderick", "Brian"]
# Filter with Python comprehensions:
prisoners = ["Roger", "Roderick", "Brian"]
release prisoner for prisoner in prisoners when prisoner[0] is "R"
# Iterate over properties in objects.
# Instead of the in keyword, use of.
names = sam: seaborn, donna: moss
alert("#{first} #{last}") for first, last of names
Using if
for null
checks in JavaScript is common, but has a few pitfalls. CoffeeScript existential operator ?
returns true
unless a variable is null
or undefined
, similar to Ruby’s nil?
.
praise if brian?
You can also use it in place of the ||
operator:
velocity = southern ? 40
If you’re using a null
check before accessing a property:
blackKnight.getLegs()?.kick()
whiteKnight.guard? us
The lines above will be compiled into:
var _ref;
if ((_ref = blackKnight.getLegs()) != null) {
_ref.kick();
}
if (typeof whiteKnight.guard === "function") {
whiteKnight.guard(us);
}
Idioms
Every language has a set of idioms and practices, which will delight veterans.
# Each
myFunction(item) for item in array
# Map
result = (item.name for item in array)
# Select
result = (item for item in array when item.name is "test")
# Select more
scores = [49, 58, 76, 82, 88, 90]
passed = (score for score in scores when score > 60)
# English style equality test
string = "migrating coconuts"
string == string # true
string is string # true
string != string # false
string isnt string # false
hash or= {} # hash || (hash = {});
has ?= {} # if (typeof hash !== undefined && hash !== null) {
# hash;
# } else {
# hash = {};
# };
# Destructing assignments
someObject = { a: 'value for a', b: 'value for b' }
{ a, b } = someObject
console.log "a is '#{a}', b is '#{b}'"
Conclusion
CoffeeScript’s beautiful syntax is amazing. Similar ideas have been extended to other languages, such as Sass for CSS, Haml for markups, and Zen Coding, which provides a new way of writing HTML and CSS code. This kind of extension may be useful in a very very long time, just like running languages on JVM.
This article is only an introduction. For more details, especially classes of CoffeeScript, see The Little Book on CoffeeScript.
EDIT: altJS is a collection of languages and tools aimed at rebooting JavaScript and making it better. It may be very useful.