Except where otherwise noted, the contents of this document are Copyright © Marty Stepp, Jessica Miller, Victoria Kirst and Zachary Cava. All rights reserved. Any redistribution, reproduction, transmission, or storage of part or all of the contents in any form is prohibited without the author's expressed written permission.
Session and Slides by Zachary Cava
It is super simple to start working with CoffeeScript, in fact you don't even need to install anything on your computer. The team of engineers behind the language have created an amazing website that you can practice on: Coffeescript.org
You can also install the CoffeeScript interpreter with the following steps, examples given for Ubuntu:
sudo apt-get install nodejs npm
sudo npm install -g coffee-script
coffee -c <code-file>
coffee
There is one big change when coding in Coffeescript, there are no curly braces! Coffeescript uses whitespace to indent blocks, this is similar to Python and can take some getting use to. Here is a simple block of code in both JS and CS, we will talk about the other changes in a minute
JavaScript | CoffeeScript |
---|---|
function somethingNew() { var old = true; if(old) { old = false; } return old; } |
somethingNew = -> old = true if old old = false return old |
The syntax for creating a function is massively simplified
Name | Javascript Syntax | CoffeeScript Syntax |
---|---|---|
Declarations | function name() { |
name = -> |
w/ Arguments | function name(arg1, arg2) { |
name = (arg1, arg2) ->
|
w/ Defaults | function name(arg1) { if(arg1 == null) { arg1 = "default value"; } |
(arg1 = "default value") -> |
w/ Splats | function name(arg1, arg2, arg3, arg4) { |
(arg1, others...) -> |
Anonymous | function() { |
-> |
w/ Arguments | function(arg1, arg2) { |
(arg1, arg2) -> |
Declaring arrays and objects has gotten easier as well
Name | Javascript Syntax | CoffeeScript Syntax |
---|---|---|
Array Declaration | var data = [1, 2, 3, 4, 5]; |
data = [1, 2, 3, 4, 5] |
Object Declaration | var person = { name : { first : "Zachary", last : "Cava" }, email : "zcava@cs" }; |
person = name : first : "Zachary" last : "Cava" email : "zcava@cs" |
I would note that you can still declare objects with curly braces, it is typically only done when you want to use one line. Multiple lines should follow the indentation idiom
Like we said before, we signify blocks with whitespace, and if statements are the first time we will see this
Javascript Syntax | CoffeeScript Syntax |
---|---|
if(test) { console.log("hi!"); } else if(othertest) { console.log("bye!"); } else { console.log("wat!"); } |
if test console.log("hi!") else if othertest console.log("bye!") else console.log("wat!") |
var data = test ? truths : falses |
var data = if test then truthes else falses |
You may say, wait a second, turnary isn't any shorter! What the heck, this is suppose to be better!!
I promise there is a reason, we will get to it in due time young padawan
At first the changes here might feel foreign, but as you see it more and more it will grow on you
Name | Javascript Syntax | CoffeeScript Syntax |
---|---|---|
For-Loop Exclusion | for(var i = 0; i < 20; i++) { // do something with i } |
for i in [0...20] # do something with i |
For-Loop Inclusion | for(var i = 0; i <= 20; i++) { // do something with i } |
for i in [0..20] # do something with i |
[1...20]
) while inclusion uses 2 dots ([1..20]
)Coffeescript actually fixes the problems with the foreach loop that Javascript has!
Name | Javascript Syntax | CoffeeScript Syntax |
---|---|---|
For-Each Arrays | var data = [1, 2, 3, 4, 5]; for(var i = 0; i <= data.length; i++) { var item = data[i]; // do something with item } |
data = [1, 2, 3, 4, 5] for item in data # do something with item! |
For-Each Objects | var name = {first: "Zachary", last: "Cava"} for(key of name) if(name.hasOwnProperty(key)) { var value = name[key]; // do something with key/value } } |
name = {first: "Zachary", last: "Cava"} for key,value of name # do something with key/value! |
Coffeescript has a aliases or replacements for a number of common operators in Javascript:
Javascript | CoffeeScript |
---|---|
=== |
is |
!== |
isnt |
! |
not |
&& |
and |
|| |
or |
true |
true, yes, on |
false |
false, no, off |
this |
@, this |
in |
of |
if(!condition) |
unless condition |
These additions were meant to bring a very typical functional program idiom to Javascript, if you have used Python before they will look familiar
[name.toLowerCase() for name in array]
callMe(data) for data in array
[num for num in array when num isnt 0]
There are a number of times in Javascript that we want to use a value or a default if it hasn't been specified. Coffescript to the rescue!
newValue = possiblySet ? "default"
If possiblySet
has been defined elsewhere before this line, then newValue
will be set to the same value, but otherwise "default"
will be used.
This is one of the reasons that turnary cannot exist in its normal form, the question mark operator is already used!
The existential operator goes even further, you can use it to really cleanup lines where a bunch of conditions must be true.
if(data !== undefined && data.shouldCall !== undefined) { data.shouldCall(); }
Transforms to
data?.shouldCall?()
Woa! That just got crazy simpler, thats the power of CoffeeScript!
On a previous slide we saw how to do a list comprehension, but there is something much deeper at work here.
All statements are expressions (well most), and they can be prepended to a line to make them conditional, or repeated like in the case of the for-loop.
Combining the if statement with some expressions we get some very clean ways to have single line statements compacted and things like conditional returns which are common coffeescript idioms
Original | Conditional |
---|---|
if neverNegative < 0 neverNegative = 0 |
neverNegative = 0 if neverNegative < 0 |
if shouldWeStop return false |
return false if shouldWeStop |
if not shouldContinue return false |
return false unless shouldContinue |
var
keyword to declare variables like we did in Javascript
this
or @
keyword is linked to by using either ->
or =>
, but there isn't enough time to talk about it here, I defer you to the reference material provided earlier.
Automatic scoping can be hard to understand and a little tricky to work with, these two blocks of code. Are they different?
Sample 1 | Sample 2 |
---|---|
someFunc = (arg1) -> inner = -> unless coolData? coolData = "No data :(" console.log(coolData) coolData = "There is data!!!" inner() |
someFunc = (arg1) -> coolData = "There is data!!!" inner = -> unless coolData? coolData = "No data :(" console.log(coolData) inner() |
Yes! Quite different! Here are the compiled versions, notice where the variable declarations are put. You need to pay close attention until you get use to it.
Sample 1 | Sample 2 |
---|---|
someFunc = function(arg1) { var coolData, inner; inner = function() { var coolData; if (typeof coolData === "undefined" || coolData === null) { coolData = "No data :("; } return console.log(coolData); }; coolData = "There is data!!!"; return inner(); }; |
someFunc = function(arg1) { var coolData, inner; coolData = "There is data!!!"; inner = function() { if (coolData == null) { coolData = "No data :("; } return console.log(coolData); }; return inner(); }; |
The syntax for declaring a class in CoffeeScript is quite similar to Python:
class Ball # stuff goes inside the class
Nothing too complex, everything you want to be in the class you simple indent inside the class!
The constructor is a special keyword method, it can take whatever arguments you choose, and if you don't specify one it will simply do nothing.
class Ball constructor: (name) -> # do stuff in constructor #do other class stuff
Note that you can only have one constructor!
To add a method you use syntax similar to the constructor, name: (args,...) ->
class Ball constructor: (name) -> # do stuff in constructor stepAnimation: -> # update ball state for one tick
One of the big uses of classes is the ability to store related information. To do this we use the @
symbol to reference the this
object.
You don't need to declare a variable in a class before you use it, typically all variables in the class will be initialized somewhere in the constructor
class Ball constructor: (name) -> @name = name @yVelocity = 0 @top = 0
Notice in the previous example we just take in name and set the class field of the same name to that value, there is actually a shorter way to write this:
class Ball constructor: (@name) -> @yVelocity = 0 @top = 0 getName: -> return @name getVelocity: -> return @yVelocity
At this point we are going to fall away from the slides because the following graph illustrates the mind bendingness of the next topic: Inheritance