On Github tylerjohnst / coffeescript-talk
Created by Tyler Johnston / @tylerjohnst
Yes! Because sugar is awesome!
CoffeeScript requires just a few simple steps to get going:
Two major changes are going to trip up everyone getting started with Coffeescript. Functions and significant whitespace. After that, it’s just Javascript.
# CoffeeScript
titleize = (string) ->
string.charAt(0).toUpperCase() + string.slice(1)
// Javascript
var titleize;
titleize = function(string) {
return string.charAt(0).toUpperCase() + string.slice(1);
};
# CoffeeScript
$('a[data-tooltip]').tooltip
position:
my: "center bottom-20"
at: "center top"
using: (position, feedback) ->
$(this).css(position)
// Javascript
$('a[data-tooltip]').tooltip({
position: {
my: "center bottom-20",
at: "center top",
using: function(position, feedback) {
return $(this).css(position);
}
}
});
String manipulation is one of the weakest aspects of Javascript for me. CoffeeScript brings it to the 21st century with Ruby style string concatenation.
name = 'Tyler'
age = 26
height = 71
"My name is #{name}, I'm #{age} years old, and I'm #{height} inches tall."
var age, height, name;
name = 'Tyler';
age = 26;
height = 71;
"My name is " + name + ", I'm " + age + " years old, and I'm " + height + " inches tall.";
Block strings can be used to hold formatted or indentation-sensitive text. The indentation level that begins the block is maintained throughout.
html = """
<strong>
cup of coffeescript
</strong>
"""
var html;
html = "<strong>\n cup of coffeescript\n</strong>";
Short hand syntax with (arg) ->
square = (x) -> x * x
cube = (x) -> square(x) * x
var cube, square;
square = function(x) {
return x * x;
};
cube = function(x) {
return square(x) * x;
};
Tame the context of this!
Account = (customer, cart) ->
@customer = customer
@cart = cart
$('.shopping_cart').bind 'click', (event) =>
@customer.purchase @cart
var Account;
Account = function(customer, cart) {
var _this = this;
this.customer = customer;
this.cart = cart;
return $('.shopping_cart').bind('click', function(event) {
return _this.customer.purchase(_this.cart);
});
};
If we had used -> in the callback above, @customer would have referred to the undefined "customer" property of the DOM element, and trying to call purchase() on it would have raised an exception.
When used in a class definition, methods declared with the fat arrow will be automatically bound to each instance of the class when the instance is constructed.
animalCall = (sound = 'moooooo!') ->
console.log(sound)
var animalCall;
animalCall = function(sound) {
if (sound == null) {
sound = 'moooooo!';
}
return console.log(sound);
};
animalCall = (sound = 'moooooo!', animals...) ->
console.log sound, animals.join(', ')
animalCall('bark', 'dog', 'frog', 'cat')
var animalCall,
__slice = [].slice;
animalCall = function() {
var animals, sound;
sound = arguments[0], animals = 2 <= arguments.length ? __slice.call(arguments, 1) : [];
if (sound == null) {
sound = 'moooooo!';
}
return console.log(sound, animals.join(', '));
};
animalCall('bark', 'dog', 'frog', 'cat');
Lots of sugar here inspired by Ruby:
# CoffeeScript
improveMood() if singing
if happy and knowsIt
clapsHands()
chaChaCha()
else
showIt()
date = if friday then sue else jill
var date, mood;
if (singing) {
improveMood();
}
if (happy && knowsIt) {
clapsHands();
chaChaCha();
} else {
showIt();
}
date = friday ? sue : jill;
Single line iterators. All of the sugar makes my teeth hurt!
# Eat lunch.
eat(food) for food in ['toast', 'cheese', 'wine']
# Fine five course dining.
courses = ['greens', 'caviar', 'truffles', 'roast', 'cake']
menu(i + 1, dish) for dish, i in courses
# Health conscious meal.
foods = ['broccoli', 'spinach', 'chocolate']
eat(food) for food in foods when food isnt 'chocolate'
var courses, dish, food, foods, i, _i, _j, _k, _len, _len1, _len2, _ref;
_ref = ['toast', 'cheese', 'wine'];
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
food = _ref[_i];
eat(food);
}
courses = ['greens', 'caviar', 'truffles', 'roast', 'cake'];
for (i = _j = 0, _len1 = courses.length; _j < _len1; i = ++_j) {
dish = courses[i];
menu(i + 1, dish);
}
foods = ['broccoli', 'spinach', 'chocolate'];
for (_k = 0, _len2 = foods.length; _k < _len2; _k++) {
food = foods[_k];
if (food !== 'chocolate') {
eat(food);
}
}
The key difference between array and object looping is the operator. Use of for objects and in for arrays since arrays are just objects under the hood.
yearsOld = max: 10, ida: 9, tim: 11
ages = for child, age of yearsOld
"#{child} is #{age}"
var age, ages, child, yearsOld;
yearsOld = {
max: 10,
ida: 9,
tim: 11
};
ages = (function() {
var _results;
_results = [];
for (child in yearsOld) {
age = yearsOld[child];
_results.push("" + child + " is " + age);
}
return _results;
})();
CoffeeScript redefines a few Javascript operators that either don't work as expected or as shorthands.
As well as word operators, there are some other defaults:
Be more OOP with CoffeeScript!
# CoffeeScript
class StringEditor
initialize: (@string) ->
truncate: (length = 30) ->
if @string.length > length
sliced = @string.slice(0, length)
"#{sliced}..."
else
@string
new StringEditor('foobar')
// Javascript
var StringEditor;
StringEditor = (function() {
function StringEditor() {}
StringEditor.prototype.initialize = function(string) {
this.string = string;
};
StringEditor.prototype.truncate = function(length) {
var sliced;
if (length == null) {
length = 30;
}
if (this.string.length > length) {
sliced = this.string.slice(0, length);
return "" + sliced + "...";
} else {
return this.string;
}
};
return StringEditor;
})();
new StringEditor('foobar');
# CoffeeScript
class BetterStringEditor extends StringEditor
truncate: (length = 15) ->
super(string)
var BetterStringEditor, _ref,
__hasProp = {}.hasOwnProperty,
__extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
BetterStringEditor = (function(_super) {
__extends(BetterStringEditor, _super);
function BetterStringEditor() {
_ref = BetterStringEditor.__super__.constructor.apply(this, arguments);
return _ref;
}
BetterStringEditor.prototype.truncate = function(length) {
if (length == null) {
length = 15;
}
return BetterStringEditor.__super__.truncate.call(this, length);
};
return BetterStringEditor;
})(StringEditor);