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);