Loading...

This presentation is an HTML5 website

Presentation template from http://html5rocks.com/

Press PgDn to advance.

AltR runs your code.

Ctrl1 toggles the JS panel.

You can watch the full presentation here:

http://youtu.be/N4MHJiAPfsg

CoffeeScript

JavaScript without the Fail

Asbjørn Sloth Tønnesen

based on a presentation by Bodil Stokke

JavaScript

Why does it hurt so much?

In the beginning...

Brendan Eich

Inventor of JavaScript,
one dark and stormy week in 1995

WTF JS?
  • Global variables
    function plus_two(n) {
        result = n + 2;
        return result;
    }
  • The with block
    var foo = 5;
    with (obj)
        foo; // 5, unless obj.foo exists
  • Wtf is the deal with the == operator?
    23 == "23";  // yes, of course a number is the same as a string
typeof NaN
=> "number"

What happened?

  • Time constraints?
  • Corporate interference?
  • Crack cocaine?
  • Agile coaches?

John McCarthy

Inventor of Lisp, 1958

Dennis Ritchie

Co-inventor of C, 1973

Bjarne Stroustrup

Inventor of C++, 1983

Guido van Rossum

Inventor of Python, 1991

James Gosling

Inventor of Java, 1995

Brendan Eich

Inventor of JavaScript, 1995

The Facial Hair Theory

of programming language design

Anders Hejlsberg

Inventor of C#, 2001

Larry Wall

Inventor of Perl, 1987

So is there no hope for JS?

Douglas Crockford

Discovered the Good Parts, 2008

"use strict";

Strict Mode
  • Assigning to global variables throws a ReferenceError
  • The with statement no longer exists.
  • But it's still JavaScript…

Ryan Dahl

Inventor of Node.JS

The joys of async programming

server.listen(function(request, response) {
    database.open(function(connection) {
        connection.query("SELECT * FROM posts", function(cursor) {
            cursor.fetchAll(function(posts) {
                response.write(posts.forEach(function(post) {
                    return post.title;
                }));
            });
        });
    });
});

// wtf

server.listen(function(request, response) { database.open(function(connection) { connection.query("SELECT * FROM posts", function(cursor) { cursor.fetchAll(function(posts) { response.write(posts.forEach(function(post) { return post.title; })); }); }); }); });

LOL WUT?

CoffeeScript

Jeremy Ashkenas

Inventor of CoffeeScript, 2009

The Facial Hair Theory

of programming language design

Jeremy Ashkenas

Inventor of CoffeeScript, 2009

Inventor of CoffeeScript, 2009

Oh, all right then.

JavaScript syntax is bloated

JavaScript has too much boilerplate

So let's get rid of the bloat

So what happened to the curly braces?
  • CoffeeScript has significant indentation.
    if (name != "honey badger") {
      care()
    } else {
      eat_cobra()
    }
    becomes
    if name != "honey badger"
      care()
    else
      eat_cobra()
    Obvious, right?
Functions
  • A function in JavaScript:
    var sum = function(a, b) {
        return a + b;
    }
  • In CoffeeScript:
    sum = (a, b) -> a + b
  • function(a, b) becomes (a, b) ->
  • function() becomes simply ->
  • return is implied: a function always returns the value of its last expression.
Calling functions
  • Parentheses are optional:
    sum = (a, b) -> a + b
    
    sum(2, 3) # returns 5
    sum 2, 3  # returns 5
  • Except when calling a function with no arguments:
    connection.close() # calls the close function
    connection.close   # returns the function, doesn't call it
sum = (a, b) -> a + b
Forget JavaScript flaws
  • All variable declarations are local:
    foo = "bar"
    # compiles to:
    var foo;
    foo = "bar";
  • No more == weirdness:
    2 == "2"
    # compiles to:
    2 === "2" # returns false, like you'd expect
  • There is no with statement.

OK LET THAT SINK IN FOR A BIT

Declaring objects
  • In JavaScript:
    var tweet = {
        user: {
            name: "veridu",
            url: "https://veridu.com/"
        },
        text: "PONIES RULE SO HARD"
    };
  • In CoffeeScript:
    tweet =
      user:
        name: "veridu"
        url: "https://veridu.com/"
      text: "PONIES RULE SO HARD"
Declaring arrays
  • In JavaScript:
    var things_that_rule = [
        "robot unicorns",
        "honey badgers",
        "Lord Inglip"
    ];
  • In CoffeeScript:
    things_that_rule = [
        "robot unicorns"
        "honey badgers"
        "Lord Inglip"
    ]
Destructuring assignment
  • Assign variables from inside arrays:
    sum_and_difference = (a, b) -> [ a + b, a - b]
    [ sum, difference ] = sum_and_difference 5, 2
    sum # returns 7
    difference # returns 3
    
sumdiff = (a, b) -> [ a + b, a - b]
tweet = user: name: "veridu" url: "https://veridu.com/" text: "PONIES RULE SO HARD"

tweet = user: name: "veridu" url: "https://veridu.com/" text: "PONIES RULE SO HARD"
But what if
  • The if statement:
    if elvis.state == "alive"
      console.log "Hail to the King!"
    else
      console.log "You lie!"
  • It has a short form:
    if power_level > 9000 then attack() else despair()
  • It can even be used as a suffix:
    reign_in_graceland() if name == "Elvis"
The while loop
  • The basic loop:
    while count > 0
      count -= 1
  • until is like while not:
    until power_level > 9000
      power_level += 1
  • And you can put them after a statement, just like if:
    power_level++ while power_level <= 9000

Everything is an expression.

Everything is an expression
  • if works like the ternary operator:
    foo = false
    result = if foo then "foo is truthy" else "foo is falsy"
    # result is "foo is falsy"
  • while returns an array:
    counter = 0
    list = while counter < 5
      counter++
      "n" + counter
    # list is [ 'n1', 'n2', 'n3', 'n4', 'n5' ]
Everything is an expression
  • Even switch statements return something:
    result = switch request.error_code
      when 200 then "OK"
      when 301 then "don't live here anymore"
      when 404 then "not found"
      when 500 then "i maed u a page... but i eated it"
      else "unknown error call the police " + request.error_code
    
foo = false result = if foo then "truthy" else "falsy"
word = "Mudkip" word = word.slice 1
result = switch request.error_code when 200 then "OK" when 301 then "don't live here anymore" when 404 then "not found" when 500 then "i maed u a page... but i eated it" else "unknown error call the police " + request.error_code
For your convenience
  • Chained comparisons:
    if 20 < temperature < 30
      venture_outside()
  • The existential operator:
    foo = { bar: "bar" }
    if foo?.bar then console.log foo.bar
    # if (foo != null && foo.bar) { ... }
pokemon = best: "Mudkip" worst: "Magikarp"
String interpolation
  • Tired of adding strings together? We borrowed this from Ruby:
    pokemon = "Snorlax"
    console.log "Wild #{pokemon} appeared!"
    => "Wild Snorlax appeared!"
  • It's not just variable lookup, you can put real code in there:
    pokemon = "Snorlax"
    console.log "Wild #{pokemon.toUpperCase()} appeared!"
    => "Wild SNORLAX appeared!"
tweet = user: name: "veridu" url: "https://veridu.com" text: "PONIES RULE SO HARD"
Heredocs
  • Want to make really large strings? CoffeeScript doesn't mind line breaks:
    blurb = "I don't always drink beer,
             but when I do,
             I prefer Dos Equis."
    console.log blurb
    "I don't always drink beer, but when I do, I prefer Dos Equis."
  • Or use heredocs to maintain formatting and stop worrying about escaping quotes:
    html = """
           <div class="article">
             <h1>Honey Badger</h1>
             <p>Honey Badger don't care.</p>
           </div>
           """
Working with arrays
  • Proper slicing operators:
    numbers = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ]
    
    numbers[3..6] # [ 3, 4, 5, 6 ]
    numbers[7..]  # [ 7, 8, 9 ]
    numbers[..3]  # [ 0, 1, 2, 3 ]
  • And we can do splicing too:
    numbers = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ]
    
    numbers[3..6] = [ "foo", "bar" ]
    numbers # [ 0, 1, 2, "foo", "bar", 7, 8, 9 ]
                  
numbers = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ]
The for loop
  • Iterate over a list's members:
    numbers = [ 1, 2, 3, "many" ]
    for number in numbers
      console.log "I just counted to #{number}"
  • It has a suffix form too, of course:
    numbers = [ 1, 2, 3, "many" ]
    console.log "I just counted to #{number}" for number in numbers
  • And it returns a new list:
    numbers = [ 1..5 ]
    result = for number in numbers
      number + 1
    result # [ 2, 3, 4, 5, 6 ]
numbers = [ 1 .. 5 ] for number in numbers number
dictionary = "omg": "Object Management Group" "wtf": "World Taekwondo Federation" "lol": "League of Legends"
List comprehension

Concise syntax for map and filter operations:

( expression for item in array )
( expression for item in array when condition )

( expression for key, value of object )
( expression for key, value of object when condition )

dictionary = "hello": "nuqneH" "yes": "HIja'" "no": "ghobe'" "okay": "lu'" "what": "nuqjatlh"
The Kingdom of Nouns

Have you ever tried to make JavaScript act object oriented?

Shape = function(x, y) {
    this.x = x;
    this.y = y;
};

Shape.prototype.centre = function() {
    return [ this.x, this.y ];
};

Shape.prototype.area = function() {
    return 0;
};

var point = new Shape(13, 37);

class Shape constructor: (@x, @y) -> centre: -> [ @x, @y ] area: -> 0 point = new Shape 13, 37
class Shape constructor: (@x, @y) -> centre: -> [ @x, @y ] area: -> 0 class Circle extends Shape constructor: (x, y, @radius) -> super x, y circle = new Circle 13, 37, 5
OK, but can I use this anywhere?
  • In your web page:
    <script type="text/javascript" src="coffee-script.js"></script>
    
    <script type="text/coffeescript" src="my.coffee"></script>
  • In Node:
    $ npm install coffee-script
    var my_cs_module = require("./my_cs_module");
  • In Play Framework 2:
    app/assets/javascripts/main.coffee
    <script src="@routes.Assets.at("javascripts/main.js")"></script>
  • Compile and run anywhere JavaScript runs:
    $ coffee -c my_script.coffee

Thank you for listening!