CoffeeScript: Good, Bold, and with Sugar

Who am I?

Daniel Mohl
F# MVP and author of various OSS such as ExpectThat

What are we doing?

What is CoffeeScript?

  • Open source language written by Jeremy Ashkenas that compiles to JavaScript.
  • Succint, yet very readable code that uses significant white-space to determine scoping.
  • Syntax is a lot like JavaScript, but adds a lot of great syntatic sugar to make your life easier.
  • Reduces the "badparts" of JavaScript.
  • Improves your JavaScript skills.
  • Combines very well with a language like F#.
  • Little risk, since it compiles to JS. Start trying it out in your projects today!

CoffeeScript: The Good Parts

In 2008, a book by Douglas Crockford was published called JavaScript: The Good Parts.

It contains a list of the good and the "aweful parts" of JS.

A few of the bad parts fixed by CoffeeScript:

  • Global scope issue (everything is wrapped in a closure) Code
  • Reserved Words (they are automatically escaped for you) Code
  • Truthy/Falsy Values (forces === and !==) Code

CoffeeScript: The Good Parts

More of the bad parts fixed by CoffeeScript:

  • Hoisting issues (only function expressions are allowed) Code
  • Others not mentioned in Crockford's book (i.e. enforces SoC, etc.)

CoffeeScript: The Bold

  • GWT, ClosureScript, Script #, Pit, Dart, are similar but different in a key way.
  • Motto: It's just JavaScript
    This integration with existing JS libraries seamless
  • An opinionated subset of JS
  • Significant whitespace

CoffeeScript: The Sugar

Lots of syntatic sugar to make code succinct and more readable.

  • @ = this Code
  • #{variable name} = string interpolation Code
  • -> = function () {...} Code
  • => = same as ->, but also binds to the current value of "this". Code
  • """ = Allows multiline string Code

CoffeeScript: More Sugar

Lots of syntatic sugar to make code succinct and more readable.

  • Object literals Code
  • Existential operator Code
  • Regular expression support Code
  • and, or, is, isnt, splats, classes, chained comparisons, comprehensions, lots more!

Disadvantages

  • The debugging story is lacking, but work is being done to implement Source Maps (SMAP) to solve this (see http://bit.ly/wKCFYU for more information.)
  • Whitespace can be a blessing or a curse.
  • There are times when you need the JavaScript features that CoffeeScript doesn't support. Backtick to the rescue... Code
  • CoffeeScript comments are not emitted in the JS. Use backticks if you really want to see the comments.
  • It requires a compilation step.

jQuery Plugin Example

  • A fairly uninteresting jQuery plugin that wraps the jQuery UI Dialog Window.
  • Visual Studio 2010 with Mindscape Web Workbench.
  • jQuery Authoring Documentation
  • About 75% of the LOC compared to the JS version
  • Shows splats, string interpolation, destructuring assignment, etc.

Getting Your Environment Setup

Example of a full libraries written in CoffeeScript

Example with ASP.NET MVC, F#, jQuery UI, and CoffeeScript

Where to Find this Presentation?

Get these slides and examples at blog.danielmohl.com in the next day or two.

My Contact Info

Daniel Mohl
F# MVP

// Global Namespace Pollution Example
				
var i = 0;
i2 = 0;
function test() {
	return i + 2;
};				
				
// Self-Executing Anonymous Function
				
(function() {
	var i, i2, test;
	test = function() {
	    return i + 2;
	};
	i = 0;
	i2 = 0;
}).call(this);

// Use "window" to explicitly add to 
// the global namespace in CoffeeScript
				
// Example from http://bit.ly/wbdl7g

// Invalid JavaScript
var myObj;
myObj = {
    delete: "I am a keyword!"
}
myObj.class = function() {};				

				
# CoffeeScript
myObj = delete: "I am a keyword!"
myObj.class = -> 

// JavaScript after compilation				
var myObj;
myObj = {
    "delete": "I am a keyword!"
};
myObj["class"] = function() {};
				
// Great post by Elijah Manor on this issue
// http://bit.ly/x7Sfan  

var foo = false;
var bar = 0;

foo == bar // evaluates to true
				
# CoffeeScript

foo = false
bar = 0

foo is bar # evaluates to false
# can also use foo == bar
				
// Example derived from 
//     http://bit.ly/wbdl7g
// Some browsers will return
//     different results here.

addIt(2, 3);

if (true) {
    function addIt(val1, val2) {
        return val1 + val2;
    }
} else {
    function addIt(val1, val2) {
        return val1 + val2 + 1000;
    }
}

// This is due to hoisting, which also
//     affects defined variables
				
# CoffeeScript
addIt 2, 3
if true
    addIt = (val1, val2) ->
        val1 + val2
else
    addIt = (val1, val2) ->
        val1 + val2 + 1000
	
// JavaScript after compilation
var addIt;
addIt(2, 3);
if (true) {
    addIt = function(val1, val2) {
        return val1 + val2;
    };
} else {
    addIt = function(val1, val2) {
        return val1 + val2 + 1000;
    };
}	
				
# CoffeeScript
$('.someclass').click -> 
    $(@).addClass "selected"
   				
// Resulting JS
$('.someclass').click(function() {
   return $(this).addClass("selected");
});
   				
# CoffeeScript

first = "Grandma"
last = "raindeer"

title = "#{first} got run over by a #{last}."
				
// Resulting JS
var first, last, title;

first = "Grandma";
last = "raindeer";

title = first +
        " got run over by a " +
        last + ".";
   				
# CoffeeScript

# Example 1
add = (val1, val2) -> val1 + val2
add 3, 4

# Ex 2 (self-executing anonymous func)
do -> 1 + 2

# Example 3
value1 = 3
do (value1) -> value1 + 2
   				
// Resulting JS

var add, value1;

add = function(val1, val2) {
   return val1 + val2;
};
add(3, 4);

(function() {
   return 1 + 2;
})();

value1 = 3;
(function(value1) {
   return value1 + 2;
})(value1);
   				
# CoffeeScript

class KanbanBoard
  addCard: => @.log 'card added'
  log: (msg) => alert msg

board = new KanbanBoard
$('#addCard').click -> board.addCard
   				
// Resulting JS

var KanbanBoard, board;
var __bind =
    function(fn, me){
        return function(){
            return fn.apply(me, arguments);
        };
    };
KanbanBoard = (function() {
    function KanbanBoard() {
        this.log = __bind(this.log, this);
        this.addCard = __bind(this.addCard, this);
    }
    KanbanBoard.prototype.addCard = function() {
        return this.log('card added');
    };
    KanbanBoard.prototype.log = function(msg) {
        return alert(msg);
    };
    return KanbanBoard;
})();
board = new KanbanBoard;
$('#addCard').click(board.addCard);
   				
# CoffeeScript

# Without string interpolation
titleMulti = '''Grandma
               got run over
               by a raindeer.'''

# With string interpolation
first = "Grandma"
last = "raindeer"

titleMulti2 = """#{first}
                 got run over
                 by a #{last}."""
   				
// Resulting JS

// Without string interpolation
var first, last, titleMulti;
var titleMulti2;
titleMulti = 'Grandma ' +
             'got run over ' +
             'by a raindeer.';

// With string interpolation
first = "Grandma";
last = "raindeer";

titleMulti2 = first +
    " got run over by a " +
    last + ".";
   				
# CoffeeScript

`// some comment
var i = "1"`
alert (i is "1")				
				
// Resuting JavaScript

// some comment
var i = "1";
alert(i === "1");
				
# CoffeeScript

name = 
   firstName: "Daniel"
   lastName: "Mohl"

# or

name2 = firstName: "Daniel", lastName: "Mohl"   
				
// Resulting JavaScript

var name, name2;

name = {
  firstName: "Daniel",
  lastName: "Mohl"
};

name2 = {
  firstName: "Daniel",
  lastName: "Mohl"
};
				
# CoffeeScript

myObj.myProp?.myVal   
				
// Resulting JavaScript

var _ref;

if ((_ref = myObj.myProp) != null) _ref.myVal;
				
# CoffeeScript

emailPattern = /// ^ #begin of line
   ([a-z0-9_\.-]+)   #one or more letters, numbers, underscores, periods, or hyphens
   @                 #followed by an @ sign
   ([\da-z\.-]+)     #then one or more letters, numbers, underscores, periods, or hyphens
   \.                #followed by a period
   ([a-z\.]{2,6})    #followed by 2 to 6 letters or periods
   $ ///             #end of line
				
// Resulting JavaScript

var emailPattern;

emailPattern = /^([a-z0-9_\.-]+)@([\da-z\.-]+)\.([a-z\.]{2,6})$/;
				

/

#