Skip to content

Instantly share code, notes, and snippets.

@rmm5t
Created September 19, 2011 23:40
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rmm5t/2330442ab20f3115110f to your computer and use it in GitHub Desktop.
Save rmm5t/2330442ab20f3115110f to your computer and use it in GitHub Desktop.
Brief Intro to CoffeeScript

CoffeeScript

CoffeeScript is a light language that compiles down to JavaScript. Because it is a language that compiles to another language, some might classify it as a "transcompiler" instead of just a compiler. CoffeeScript's syntax is heavily inspired by Python and Ruby. For the most part, statements in CoffeeScript correspond one-to-one with their equivalent in JavaScript.

CoffeeScript was created out of love for the JavaScript language, but also out of frustration with the JavaScript syntax. The JavaScript syntax has a tendency to become cumbersome, verbose, and arguably, syntactically messy. The CoffeeScript syntax on the other hand, improves the readability and writabilty of equivalent code that would have otherwise been written in JavaScript.

CoffeeScript's succinct syntax offers developers an enjoyable experience, but it also includes other features that makes development more natural. For example, it includes a simple syntax for object oriented programming, and it includes Python-like array and object comprehensions that make it easier to iterate and interact with lists and objects.

Getting Started

First, we must install the CoffeeScript compiler. One of the easiest ways to do this is to first install Node.js and the Node Package Manager (NPM). The following example uses Homebrew for Mac OS X.

$ brew install node
$ curl http://npmjs.org/install.sh | sh

For other means of installing the Node Package Manager, see http://npmjs.org.

After we have the Node Package Manager installed, we can use it to install the CoffeeScript compiler.

$ npm install --global coffee

This will install a new executable called coffee that we can use to both compile CoffeeScript scripts to JavaScript or to run an interactive CoffeeScript REPL (Read Eval Print Loop).

Hello!

Let's start by playing with CoffeeScript's interactive REPL.

$ coffee

This will open a coffee> prompt waiting for a line of CoffeeScript to interpret. Let's first create a new variable.

coffee> name = "John Doe"

Upon hitting Enter, CoffeeScript will print the return value of the last statement. In this case, it's the value of the variable.

coffee> name = "John Doe"
John Doe

Next, let's use that variable and print it along with a message.

coffee> console.log "Hello, #{name}!"
Hello, John Doe!

Here, we used the name variable that we created and used it inside another string that we passed to console.log function. The console.log printed the resulting string to the screen. This variable substitution within a string is called string interpolation, and CoffeeScript borrows this behavior from the Ruby language.

Compiling to JavaScript

Let's now use the coffee command to compile some CoffeeScript to JavaScript. Create a file called hello.coffee that contains the same commands we used above.

# hello.coffee
name = "John Doe"
console.log "Hello, #{name}!"

By passing this file to the coffee command, we can convert this to JavaScript.

$ coffee -c hello.coffee

This will create a new file named hello.js. Let's take a look.

$ cat hello.js
(function() {
  var name;
  name = "John Doe";
  console.log("Hello, " + name + "!");
}).call(this);

If you're familiar with JavaScript, you might notice that the resulting code is wrapped in an anonymous function. CoffeeScript does this to help protect the global namespace in JavaScript. We'll touch on this more when we talk about variables.

Significant Whitespace

Just like Python, CoffeeScript has significant whitespace. There is no need for curly braces or semi-colons, but you do need to ensure proper 2-space nesting conventions.

if sheepInMeadow and cowsInTheCorn
  blowHorn()
else
  lookAfterSheep()

Variables

In our first hello.coffee example, we saw that CoffeeScript compiled our example while wrapping everything in an anonymous function. Variable scoping in JavaScript can sometimes be a tricky thing to master, so CoffeeScript takes some precautions to make sure our variables are only defined (or in scope) when we expect them to be.

In JavaScript, when we want to scope a variable only to the local function block, we must prefix each local variable declaration with the var keyword.

// JavaScript local variable
var local = "local variable";

If we do not remember to do this, we instead create global variables which might accidentally clobber another global variable somewhere else.

// Whoops, a Global JavaScript variable.
global = "global variable";

Because global variables can lead to more confusing and buggy code, we try to avoid them as much as possible. CoffeeScript avoids this problem by making all variable declarations not only local to the current function body, but even to the file itself. This is why compiled CoffeeScript wraps each file in an anonymous function.

Let's define several variables in CoffeeScript and see what the resulting JavaScript looks like.

# CoffeeScript
one   = 1
two   = 2
three = 3

The resulting JavaScript automatically wraps everything in an anonymous function to prevent defining global variables and also automatically prefixes the variable definitions with the var keyword.

// Equivalent JavaScript
(function() {
  var one, three, two;
  one = 1;
  two = 2;
  three = 3;
}).call(this);

This variable safety is one way that CoffeeScript handles lexical scoping of variables for us. Lexical scoping in CoffeeScript works effectively the same as Ruby’s scope for local variables.

Functions

Functions in CoffeeScript are defined by a list of parameters, an arrow, and the function body. Let's define a function variable that represents the area of two sides to a rectangle.

area = (x, y) -> x * y

The function body can also exist on subsequent indented lines.

area = (x, y) ->
  x * y

All functions implicitly return the last value within the function body, so we could write a new square function the reuses the area function above.

square = (x) -> area(x, y)

If a function has no parameters, you can forego the list of parameters in the function definition.

noop = ->
  console.log("I'm a function that doesn't do much.")

Default Parameter Values

We can also specify default values for each function parameter.

hello = (name = "John) ->
  console.log("Hello, #{name}!")

hello()         # prints "Hello, John!"
hello("Jane")   # prints "Hello, Jane!"

When we call functions with parameters, the parentheses around the parameter (or argument) list is optional.

hello "Jane"    # prints "Hello, Jane!"

However, when we call a functions without any parameters, we must still supply the parentheses.

hello()         # The parentheses are required

Splat Arguments

Function Binding

String and RegExp Interpolation

Destructuring Assignment

With Arrays

With Object Literals

Existential Operator

OOP

Comprehensions

With Arrays

With Object Literals

Slicing and Splicing

Other Syntax Constructs

Switch/When/Else

While/Until/Loop

Try/Catch/Finally

Chained Comparison

Multiline Strings, Herdocs, Block Comments

Extended Regular Expressions

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment