Skip to content

Instantly share code, notes, and snippets.

@STRd6
Created May 8, 2013 20:38
Show Gist options
  • Save STRd6/5543472 to your computer and use it in GitHub Desktop.
Save STRd6/5543472 to your computer and use it in GitHub Desktop.
Some notes about CoffeeScript

The Basics

Variables

greeting = "hello"
location = "world"

x = 8
y = 3

z = x + y

Functions

Defining Functions

noop = ->

identity = (x) ->
  x

successor = (x) ->
  x + 1

CoffeeScript always returns the last line of the function, so you can omit return statements.

Calling Functions

noop()

identity(5)

successor(0)

You can omit the parenthesis only if you supply an argument:

y = successor 1 # y is 2

z = noop #! A reference to the function, not the result of invoking the function

It can get crazy if you have many applications in a row:

identity successor successor successor successor 1
identity(successor(successor(successor(successor(1)))))

So don't go nuts.

Passing arguments to jQuery methods is easy:

# Simple callback
$.getJSON "/data", (result) ->
  console.log result

# Multiple options
$.post "/data",
  success: ->
    alert "It worked!"
  error: ->
    alert "I am error"

Functions can also have default arguments:

greet = (name = "you") ->
  "Hey #{name}!"

Strings

Interpolation

x = 7
y = 2

alert "#{x} + #{y} is #{x + y}!"

Block strings

markdown = """
  Interpolation

      x = 7
      y = 2

      alert "\#{x} + \#{y} is \#{x + y}!"
"""

Interpolation will occur in block strings so you can prevent that with \#

Objects

JS objects literals are pretty easy to declare:

var duder;

duder = {
  name: "The Dude",
  greet: function() {
    return "Hi I'm " + this.name;
  }
}

But CoffeeScript does quite a lot better:

duder =
  name: "The Dude"
  greet: ->
    "Hi I'm #{@name}"

You can omit braces and commas

@name is shorthand for this.name

It better stylistically to use @thing instead of this.thing though both work.

It looks a little weird in isolation though:

return @

So being explicit is probably better:

return this

Iteration

Object properties

fruitPrices =
  apple: 2.01
  orange: 1.50
  banana: 3.29
  coconut: 5.00

for fruit, price of fruitPrices
  console.log "#{fruit} costs #{price}"

Lists

ingredients = [
  "rye"
  "swiss"
  "saurkraut"
  "pastrami"
  "dijon"
]

for ingredient, index in ingredients
  console.log "#{index + 1}: #{ingredient}"

Logical Operators

is and == are exactly the same.

a is b
a == b

Likewise with or and ||, and and and &&

a or b
a || b

a and b
a && b

Existence Operator

if a?
  "It exists!"

x = y ? 0

The existence operator tells you whether a variable exists (it is defined and not null).

It can also be used initialize a default value.

Soaks

a?.radical?()

Attempt to access properties or call functions if we're not sure whether they should exist and if we don't care if they don't.

Soaks are like sponges that soak up nulls.

Splats

awardMedals = (first, second, third, losers...) ->

awardMedals(
  "Joe E. Winner"
  "Second Place"
  "Third Place"
  "Loser 1"
  "Loser 2"
)

puts = (args...) ->
  console.log(args...)

Destructuring Assignment

Destructuring assignment is a great feature of CoffeeScript, it can really dry out some repetitive code:

# Without destructuring assignment
name = user.name
address = user.address
phoneNumber = user.phoneNumber

# With destructuring assignment
{name, address, phoneNumber} = user

Pretty radical.

It works with arrays too:

[first, rest...] = list

You can even use it in method arguments:

drawCircle: ({x, y, radius, color}) ->
  context.beginPath()
  context.arc(x, y, radius, 0, Math.TAU, true)
  context.closePath()

  if color
    @fillColor(color)
    context.fill()

Destructuring assignment allows for actual named arguments. To call the function in the prevous example:

drawCircle
  x: 50
  y: 50
  radius: 250
  color: "blue"

Notice how each argument has a name and that they can appear in any order.

This technique can be extended to allow for complex methods that can take a variety of arguments:

drawCircle: ({x, y, radius, position, color, stroke, circle}) ->
  {x, y, radius} = circle if circle
  {x, y} = position if position

  radius = radius.clamp(0, Infinity)

  context.beginPath()
  context.arc(x, y, radius, 0, Math.TAU, true)
  context.closePath()

  if color
    @fillColor(color)
    context.fill()

  if stroke
    @strokeColor(stroke.color)
    @lineWidth(stroke.width)
    context.stroke()

  return @

Take a look at all the different ways we can call the method:

# Draw a large orange circle
canvas.drawCircle
  radius: 30
  position: Point(100, 75)
  color: "orange"

# Draw a blue circle with radius 10 at (25, 50)
# and a red stroke
canvas.drawCircle
  x: 25
  y: 50
  radius: 10
  color: "blue"
  stroke:
    color: "red"
    width: 1

# Create a circle object to set up the next examples
circle =
  radius: 20
  x: 50
  y: 50

# Draw a given circle in yellow
canvas.drawCircle
  circle: circle
  color: "yellow"

# Draw the circle in green at a different position
canvas.drawCircle
  circle: circle
  position: Point(25, 75)
  color: "green"

The do keyword

Hiding

do ->
  x = 5 # Look at me I'm in my own scope!

console.log(x)

Shadowing

x = 10
do (x=5) ->
  console.log x

console.log x

Example: jQuery Plugin

do ($=jQuery) ->
  $.fn.takeClass = (name) ->
    @addClass(name).siblings().removeClass(name)

    return this

Here we have alias jQuery as $ so that we can take advantage of the short

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