CoffeeScript is a little language that compiles down to JavaScript. The syntax is inspired by Ruby and Python, and implements many features from those two languages. This book is designed to help you learn CoffeeScript, understand best practices and start building awesome client side applications. The book is little, only five chapters, but that's rather apt as CoffeeScript is a little language too.
If you have any errata or suggestions, please don't hesitate to open a ticket on the book's GitHub page. Readers may also be interested in JavaScript Web Applications by O'Reilly, a book that explores rich JavaScript applications and moving state to the client side.
-
CoffeeScript is a little language that compiles down to JavaScript. The syntax is inspired by Ruby and Python, and implements many features from those two languages. This book is designed to help you learn CoffeeScript, understand best practices and start building awesome client side applications. The book is little, only five chapters, but that's rather apt as CoffeeScript is a little language too.
+
So let's dive right into it; why is CoffeeScript better than writing pure JavaScript? Well for a start, there's less code to write - CoffeeScript is very succinct, and takes white-space into account. In my experience this reduces code by a third to a half of the original pure JavaScript. In addition, CoffeeScript has some neat features, such as array comprehensions, prototype aliases and classes that further reduce the amount of typing you need to do.
More importantly though, JavaScript has a lot of skeletons in its closet which can often trip up inexperienced developers. CoffeeScript neatly sidesteps these, by only exposing a curated selection of JavaScript features, fixing many of the language's oddities.
-
If you have any errata or suggestions, please don't hesitate to open a ticket on the book's GitHub page. Readers may also be interested in JavaScript Web Applications by O'Reilly, a book that explores rich JavaScript applications and moving state to the client side.
+
CoffeeScript is not a superset of JavaScript, so although you can use external JavaScript libraries from inside CoffeeScript, you'll get syntax errors if you compile JavaScript as-is, without converting it. The compiler converts CoffeeScript code into its counterpart JavaScript, there's no interpretation at runtime.
-
So let's dive right into it; why is CoffeeScript better than writing pure JavaScript? Well for a start, there's less code to write - CoffeeScript is very succinct, and takes white-space into account. In my experience this reduces code by a third to a half of the original pure JavaScript. In addition, CoffeeScript has some neat features, such as array comprehensions, prototype aliases and classes that further reduce the amount of typing you need to do.
+
First to get some common fallacies out the way. You will need to know JavaScript in order to write CoffeeScript, as runtime errors require JavaScript knowledge. However, having said that, runtime errors are usually pretty obvious, and so far I haven't found mapping JavaScript back to CoffeeScript to be an issue. The second problem I've often heard associated with CoffeeScript is speed; i.e. the code produced by the CoffeeScript compiler would run slower than it's equivalent written in pure JavaScript. In practice though, it turns out this isn't a problem either. CoffeeScript tends to run as fast, or faster than hand-written JavaScript.
-
More importantly though, JavaScript has a lot of skeletons in its closet which can often trip up inexperienced developers. CoffeeScript neatly sidesteps these, by only exposing a curated selection of JavaScript features, fixing many of the language's oddities.
+
What are the disadvantages of using CoffeeScript? Well, it introduces another compile step between you and your JavaScript. CoffeeScript tries to mitigate the issue as best it can by producing clean and readable JavaScript, and with its server integrations which automate compilation. The other disadvantage, as with any new language, is the fact that the community is still small at this point, and you'll have a hard time finding fellow collaborators who already know the language. CoffeeScript is quickly gaining momentum though, and its IRC list is well staffed; any questions you have are usually answered promptly.
-
CoffeeScript is not a superset of JavaScript, so although you can use external JavaScript libraries from inside CoffeeScript, you'll get syntax errors if you compile JavaScript as-is, without converting it. The compiler converts CoffeeScript code into its counterpart JavaScript, there's no interpretation at runtime.
+
CoffeeScript is not limited to the browser, and can be used to great effect in server side JavaScript implementations, such as Node.js. Additionally, CoffeeScript is getting much wider use and integration, such as being a default in Rails 3.1. Now is definitely the time to jump on the CoffeeScript train. The time you invest in learning about the language now will be repaid by major time savings later.
-
First to get some common fallacies out the way. You will need to know JavaScript in order to write CoffeeScript, as runtime errors require JavaScript knowledge. However, having said that, runtime errors are usually pretty obvious, and so far I haven't found mapping JavaScript back to CoffeeScript to be an issue. The second problem I've often heard associated with CoffeeScript is speed; i.e. the code produced by the CoffeeScript compiler would run slower than it's equivalent written in pure JavaScript. In practice though, it turns out this isn't a problem either. CoffeeScript tends to run as fast, or faster than hand-written JavaScript.
+
Initial setup
-
What are the disadvantages of using CoffeeScript? Well, it introduces another compile step between you and your JavaScript. CoffeeScript tries to mitigate the issue as best it can by producing clean and readable JavaScript, and with its server integrations which automate compilation. The other disadvantage, as with any new language, is the fact that the community is still small at this point, and you'll have a hard time finding fellow collaborators who already know the language. CoffeeScript is quickly gaining momentum though, and its IRC list is well staffed; any questions you have are usually answered promptly.
+
One of the easiest ways to initially play around with the library is to use it right inside the browser. Navigate to http://coffeescript.org and click on the Try CoffeeScript tab. The site uses a browser version of the CoffeeScript compiler, converting any CoffeeScript typed inside the left panel, to JavaScript in the right panel.
-
CoffeeScript is not limited to the browser, and can be used to great effect in server side JavaScript implementations, such as Node.js. Additionally, CoffeeScript is getting much wider use and integration, such as being a default in Rails 3.1. Now is definitely the time to jump on the CoffeeScript train. The time you invest in learning about the language now will be repaid by major time savings later.
+
In fact you can use browser-based compiler yourself, by including this script in a page, marking up any CoffeeScript script tags with the correct type.
-
Initial setup
-
-
One of the easiest ways to initially play around with the library is to use it right inside the browser. Navigate to http://coffeescript.org and click on the Try CoffeeScript tab. The site uses a browser version of the CoffeeScript compiler, converting any CoffeeScript typed inside the left panel, to JavaScript in the right panel.
-
-
In fact you can use browser-based compiler yourself, by including this script in a page, marking up any CoffeeScript script tags with the correct type.
Obviously, in production, you don't want to be interpreting CoffeeScript at runtime, as it'll slow thing up for your clients, so CoffeeScript offers a Node.js compiler to pre-process CoffeeScript files.
+# Some CoffeeScript
+</script>
-
To install it, first make sure you have a working copy of the latest stable version of Node.js, and npm (the Node Package Manager). You can then install CoffeeScript with npm:
+
Obviously, in production, you don't want to be interpreting CoffeeScript at runtime, as it'll slow thing up for your clients, so CoffeeScript offers a Node.js compiler to pre-process CoffeeScript files.
-
npm install coffee-script
-
+
To install it, first make sure you have a working copy of the latest stable version of Node.js, and npm (the Node Package Manager). You can then install CoffeeScript with npm:
-
This will give you a coffee executable. If you execute it without any command line options, it'll give you the CoffeeScript console, which you can use to quickly execute CoffeeScript statements. To pre-process files, pass the --compile option.
+
npm install coffee-script
-
coffee --compile my-script.coffee
-
+
This will give you a coffee executable. If you execute it without any command line options, it'll give you the CoffeeScript console, which you can use to quickly execute CoffeeScript statements. To pre-process files, pass the --compile option.
-
If --output is not specified, CoffeeScript will write to a JavaScript file with the same name, in this case my-script.js. This will overwrite any existing files, so be careful you're not overwriting any JavaScript files unintentionally. For a full list of the command line options available, pass --help.
+
coffee --compile my-script.coffee
-
As you can see above, the default extension of CoffeeScript files is .coffee. Amongst other things, this will allow text editors like TextMate to work out which language the file contains, giving it the appropriate syntax highlighting. By default, TextMate doesn't include support for CoffeeScript, but you can easily install the bundle to do so.
+
If --output is not specified, CoffeeScript will write to a JavaScript file with the same name, in this case my-script.js. This will overwrite any existing files, so be careful you're not overwriting any JavaScript files unintentionally. For a full list of the command line options available, pass --help.
-
If all this compilation seems like a bit of an inconvenience and bother, that's because it is. We'll be getting onto ways to solve this by automatically compiling CoffeeScript files when they're first requested, but first lets take a look at the languages's syntax.
+
As you can see above, the default extension of CoffeeScript files is .coffee. Amongst other things, this will allow text editors like TextMate to work out which language the file contains, giving it the appropriate syntax highlighting. By default, TextMate doesn't include support for CoffeeScript, but you can easily install the bundle to do so.
-
-
-
+
If all this compilation seems like a bit of an inconvenience and bother, that's because it is. We'll be getting onto ways to solve this by automatically compiling CoffeeScript files when they're first requested, but first lets take a look at the languages's syntax.
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/coffeescript/02_syntax.html b/coffeescript/02_syntax.html
index 758ed21..8d60f7e 100644
--- a/coffeescript/02_syntax.html
+++ b/coffeescript/02_syntax.html
@@ -1,200 +1,180 @@
-
-
-The Little Book on CoffeeScript - Syntax
-
-
-
-
-
-
-
-
-
-
Firstly, before we get any further into this section, I want to reiterate that while CoffeeScript's syntax is often identical with JavaScript's, it's not a superset, and therefore some JavaScript keywords, such as function and var aren't permitted, and will throw syntax errors. If you're writing a CoffeeScript file, it needs to be pure CoffeeScript; you can't intermingle the two languages.
-
-
Why isn't CoffeeScript a superset? Well, the very fact that whitespace is significant in CoffeeScript programs prevents it being a superset. And, once that's decision's been made, the team decided you might as well go the full hog and deprecate some JavaScript keywords and features in the name of simplicity and in an effort to reduce many commonly occurring bugs.
-
-
What I find mind-blowing, in a meta sort of way, is that the CoffeeScript interpreter itself is actually written in CoffeeScript. It looks like the chicken or egg paradox has finally been solved!
-
-
Right, so firstly let's tackle the basic stuff. There are no semicolons in CoffeeScript, it'll add them automatically for you upon compilation. Semicolons were the cause of much debate in the JavaScript community, and behind some weird interpreter behavior. Anyway, CoffeeScript resolves this problem for you by simply removing semi-colons from its syntax, adding them as needed behind the scenes.
-
-
Comments are in the same format as Ruby comments, starting with a hash character.
-
-
# A comment
-
-
-
Multiline comments are also supported, and are brought forward to the generated JavaScript. They're enclosed by three hash characters.
-
-
-
-
###
+
+
+ The Little Book on CoffeeScript - Introduction
+
+
+
+
+
+
+
+
Firstly, before we get any further into this section, I want to reiterate that while CoffeeScript's syntax is often identical with JavaScript's, it's not a superset, and therefore some JavaScript keywords, such as function and var aren't permitted, and will throw syntax errors. If you're writing a CoffeeScript file, it needs to be pure CoffeeScript; you can't intermingle the two languages.
+
Why isn't CoffeeScript a superset? Well, the very fact that whitespace is significant in CoffeeScript programs prevents it being a superset. And, once that's decision's been made, the team decided you might as well go the full hog and deprecate some JavaScript keywords and features in the name of simplicity and in an effort to reduce many commonly occurring bugs.
+
What I find mind-blowing, in a meta sort of way, is that the CoffeeScript interpreter itself is actually written in CoffeeScript. It looks like the chicken or egg paradox has finally been solved!
+
+
Right, so firstly let's tackle the basic stuff. There are no semicolons in CoffeeScript, it'll add them automatically for you upon compilation. Semicolons were the cause of much debate in the JavaScript community, and behind some weird interpreter behavior. Anyway, CoffeeScript resolves this problem for you by simply removing semi-colons from its syntax, adding them as needed behind the scenes.
+
+
Comments are in the same format as Ruby comments, starting with a hash character.
+
+
# A comment
+
+
Multiline comments are also supported, and are brought forward to the generated JavaScript. They're enclosed by three hash characters.
+
+
+
+
###
A multiline comment, perhaps a LICENSE.
-###
-
+###
-
As I briefly alluded to, whitespace is significant in CoffeeScript. In practice, this means that you can replace curly brackets ({}) with a tab. This takes inspiration from Python's syntax, and has the excellent side effect of ensuring that your script is formatted in a sane manner, otherwise it won't even compile!
+
As I briefly alluded to, whitespace is significant in CoffeeScript. In practice, this means that you can replace curly brackets ({}) with a tab. This takes inspiration from Python's syntax, and has the excellent side effect of ensuring that your script is formatted in a sane manner, otherwise it won't even compile!
-
Variables & Scope
+
Variables & Scope
-
CoffeeScript fixes one of the major bugbears with JavaScript, global variables. In JavaScript, it's all too easy to accidentally declare a global variable by forgetting to include var before the variable assignment. CoffeeScript solves this by simply removing global variables. Behind the scenes, CoffeeScript wraps up scripts with an anonymous function, keeping the local context, and automatically prefixes all variable assignments with var. For example, take this simple variable assignment in CoffeeScript:
+
CoffeeScript fixes one of the major bugbears with JavaScript, global variables. In JavaScript, it's all too easy to accidentally declare a global variable by forgetting to include var before the variable assignment. CoffeeScript solves this by simply removing global variables. Behind the scenes, CoffeeScript wraps up scripts with an anonymous function, keeping the local context, and automatically prefixes all variable assignments with var. For example, take this simple variable assignment in CoffeeScript:
-
+
-
myVariable = "test"
-
+
myVariable = "test"
-
Notice the dark grey box in the top right of the code example above. Give that a click, and the code will toggle between CoffeeScript and the compiled JavaScript. This is rendered right inside the page at runtime, so you assured the compiled output is accurate.
+
Notice the dark grey box in the top right of the code example above. Give that a click, and the code will toggle between CoffeeScript and the compiled JavaScript. This is rendered right inside the page at runtime, so you assured the compiled output is accurate.
-
As you can see, the variable assignment is kept completely local, it's impossible to accidentally create a global variable. CoffeeScript actually takes this a step further, and makes it impossible to shadow a higher-level variable. This goes a great deal to prevent some of the most common mistakes developers make in JavaScript.
+
As you can see, the variable assignment is kept completely local, it's impossible to accidentally create a global variable. CoffeeScript actually takes this a step further, and makes it impossible to shadow a higher-level variable. This goes a great deal to prevent some of the most common mistakes developers make in JavaScript.
-
However, sometimes it's useful to create global variables. You can either do this by directly setting them as properties on the global object (window in browsers), or with the following pattern:
+
However, sometimes it's useful to create global variables. You can either do this by directly setting them as properties on the global object (window in browsers), or with the following pattern:
-
+
-
exports = this
-exports.MyVariable = "foo-bar"
-
+
exports = this
+exports.MyVariable = "foo-bar"
-
In the root context, this is equal to the global object, and by creating a local exports variable you're making it really obvious to anyone reading your code exactly which global variables a script is creating. Additionally, it paves the way for CommonJS modules, which we're going to cover later in the book.
+
In the root context, this is equal to the global object, and by creating a local exports variable you're making it really obvious to anyone reading your code exactly which global variables a script is creating. Additionally, it paves the way for CommonJS modules, which we're going to cover later in the book.
-
Functions
+
Functions
-
CoffeeScript removes the rather verbose function statement, and replaces it with a thin arrow: ->. Functions can be one liners or indented on multiple lines. The last expression in the function is implicitly returned. In other words, you don't need to use the return statement unless you want to return earlier inside the function.
+
CoffeeScript removes the rather verbose function statement, and replaces it with a thin arrow: ->. Functions can be one liners or indented on multiple lines. The last expression in the function is implicitly returned. In other words, you don't need to use the return statement unless you want to return earlier inside the function.
-
With that in mind, let's take a look at an example:
+
With that in mind, let's take a look at an example:
-
+
-
func = -> "bar"
-
+
func = -> "bar"
-
You can see in the resultant compilation, the -> is turned into a function statement, and the "bar" string is automatically returned.
+
You can see in the resultant compilation, the -> is turned into a function statement, and the "bar" string is automatically returned.
-
As mentioned earlier, there's no reason why we can't use multiple lines, as long we indent the function body properly.
+
As mentioned earlier, there's no reason why we can't use multiple lines, as long we indent the function body properly.
-
+
-
func = ->
+
func = ->
# An extra line
- "bar"
-
+ "bar"
-
Function arguments
+
Function arguments
-
How about specifying arguments? Well, CoffeeScript lets you do that by specifying arguments in parentheses before the arrow.
+
How about specifying arguments? Well, CoffeeScript lets you do that by specifying arguments in parentheses before the arrow.
-
+
-
times = (a, b) -> a * b
-
+
times = (a, b) -> a * b
-
CoffeeScript supports default arguments too, for example:
+
CoffeeScript supports default arguments too, for example:
-
+
-
times = (a = 1, b = 2) -> a * 2
-
+
times = (a = 1, b = 2) -> a * 2
-
You can also use splats to accept multiple arguments, denoted by ...
+
You can also use splats to accept multiple arguments, denoted by ...
-
+
-
sum = (nums...) ->
+
sum = (nums...) ->
result = 0
nums.forEach (n) -> result += n
- result
-
+ result
-
In the example above, nums is an array of all the arguments passed to the function. It's not an arguments object, but rather a real array, so you don't need to concern yourself with Array.prototype.splice or jQuery.makeArray() if you want to manipulate it.
+
In the example above, nums is an array of all the arguments passed to the function. It's not an arguments object, but rather a real array, so you don't need to concern yourself with Array.prototype.splice or jQuery.makeArray() if you want to manipulate it.
Functions can be invoked exactly as in JavaScript, with parens (), apply() or call(). However, like Ruby, CoffeeScript will automatically call functions if they are invoked with at least one argument.
+
Function invocation
-
+
Functions can be invoked exactly as in JavaScript, with parens (), apply() or call(). However, like Ruby, CoffeeScript will automatically call functions if they are invoked with at least one argument.
-
a = "Howdy!"
+
+
a = "Howdy!"
alert a
# Equivalent to:
alert(a)
alert inspect a
# Equivalent to:
-alert(inspect(a))
-
+alert(inspect(a))
-
Although parenthesis is optional, I'd recommend using it if it's not immediately obvious what's being invoked, and with which arguments. In the last example, with inspect, I'd definitely recommend wrapping at least the inspect invocation in parens.
+
Although parenthesis is optional, I'd recommend using it if it's not immediately obvious what's being invoked, and with which arguments. In the last example, with inspect, I'd definitely recommend wrapping at least the inspect invocation in parens.
-
+
-
alert inspect(a)
-
+
alert inspect(a)
-
If you don't pass any arguments with an invocation, CoffeeScript has no way of working out if you intend to invoke the function, or just treat it like a variable. In this respect, CoffeeScript's behavior differs from Ruby which always invokes references to functions, and more similar to Python's. This has been the source of a few errors in my CoffeeScript programs, so it's worth keeping an eye out for cases where you intend to call a function without any arguments, and include parenthesis.
+
If you don't pass any arguments with an invocation, CoffeeScript has no way of working out if you intend to invoke the function, or just treat it like a variable. In this respect, CoffeeScript's behavior differs from Ruby which always invokes references to functions, and more similar to Python's. This has been the source of a few errors in my CoffeeScript programs, so it's worth keeping an eye out for cases where you intend to call a function without any arguments, and include parenthesis.
-
Function context
+
Function context
-
Context changes are rife within JavaScript, especially with event callbacks, so CoffeeScript provides a few helpers to manage this. One such helper is a variation on ->, the fat arrow function: =>
+
Context changes are rife within JavaScript, especially with event callbacks, so CoffeeScript provides a few helpers to manage this. One such helper is a variation on ->, the fat arrow function: =>
-
Using the fat arrow instead of the thin arrow ensures that the function context will be bound to the local one. For example:
+
Using the fat arrow instead of the thin arrow ensures that the function context will be bound to the local one. For example:
The reason you might want to do this, is that callbacks from addEventListener() are executed in the context of the element, i.e. this equals the element. If you want to keep this equal to the local context, without doing a self = this dance, fat arrows are the way to go.
+
The reason you might want to do this, is that callbacks from addEventListener() are executed in the context of the element, i.e. this equals the element. If you want to keep this equal to the local context, without doing a self = this dance, fat arrows are the way to go.
-
This binding idea is a similar concept to jQuery's proxy() or ES5'sbind() functions.
+
This binding idea is a similar concept to jQuery's proxy() or ES5'sbind() functions.
-
Object literals & array definition
+
Object literals & array definition
-
Object literals can be specified exactly as in JavaScript, with a pair of braces and key/value statements. However, like with function invocation, CoffeeScript makes the braces optional. In fact, you can also use indentation and new lines instead of comma separation.
+
Object literals can be specified exactly as in JavaScript, with a pair of braces and key/value statements. However, like with function invocation, CoffeeScript makes the braces optional. In fact, you can also use indentation and new lines instead of comma separation.
-
+
-
object1 = {one: 1, two: 2}
+
object1 = {one: 1, two: 2}
# Without braces
object2 = one: 1, two: 2
# Using new lines instead of commas
object3 =
- one: 1
- two: 2
+one: 1
+two: 2
-User.create(name: "John Smith")
-
+User.create(name: "John Smith")
-
Likewise, arrays can use whitespace instead of comma separators, although the square brackets ([]) are still required.
+
Likewise, arrays can use whitespace instead of comma separators, although the square brackets ([]) are still required.
As you can see in the example above, CoffeeScript has also stripped the trailing comma in array3, another common source of cross-browser errors.
+
As you can see in the example above, CoffeeScript has also stripped the trailing comma in array3, another common source of cross-browser errors.
-
Flow control
+
Flow control
-
The convention of optional parentheses continues with CoffeeScript's if and else keywords.
+
The convention of optional parentheses continues with CoffeeScript's if and else keywords.
-
+
-
if true == true
+
if true == true
"We're ok"
if true != true then "Panic"
# Equivalent to:
# (1 > 0) ? "Ok" : "Y2K!"
-if 1 > 0 then "Ok" else "Y2K!"
-
+if 1 > 0 then "Ok" else "Y2K!"
-
As you can see above, if the if statement is on one line, you'll need to use the then keyword, so CoffeeScript knows when the block begins. Conditional operators (?:) are not supported, instead you should use a single line if/else statement.
+
As you can see above, if the if statement is on one line, you'll need to use the then keyword, so CoffeeScript knows when the block begins. Conditional operators (?:) are not supported, instead you should use a single line if/else statement.
-
CoffeeScript also includes a Ruby idiom of allowing suffixed if statements.
+
CoffeeScript also includes a Ruby idiom of allowing suffixed if statements.
-
+
-
alert "It's cold!" if heat < 5
-
+
alert "It's cold!" if heat < 5
-
Instead of using the exclamation mark (!) for negation, you can also use the not keyword - which can sometimes make your code more readable as exclamation marks can be easy to miss.
+
Instead of using the exclamation mark (!) for negation, you can also use the not keyword - which can sometimes make your code more readable as exclamation marks can be easy to miss.
-
+
-
if not true then "Panic"
-
+
if not true then "Panic"
-
In the example above, we could also use the CoffeeScript's unless statement, the opposite of if.
+
In the example above, we could also use the CoffeeScript's unless statement, the opposite of if.
-
+
-
unless true
- "Panic"
-
+
unless true
+ "Panic"
-
In a similar fashion to not, CoffeeScript also introduces the is statement, which translates to ===.
+
In a similar fashion to not, CoffeeScript also introduces the is statement, which translates to ===.
-
+
-
if true is 1
- "Type coercion fixed!"
-
+
if true is 1
+ "Type coercion fixed!"
-
You may have noticed in the examples above, that CoffeeScript is converting == operators into === and != into !==. This is one of my favorite features to the language, and yet one of the most simple. What's the reasoning behind this? Well frankly JavaScript's type coercion is a bit odd, and its equality operator coerces types in order to compare them, leading to some confusing behaviors and the source of many bugs.
+
You may have noticed in the examples above, that CoffeeScript is converting == operators into === and != into !==. This is one of my favorite features to the language, and yet one of the most simple. What's the reasoning behind this? Well frankly JavaScript's type coercion is a bit odd, and its equality operator coerces types in order to compare them, leading to some confusing behaviors and the source of many bugs.
The solution is to use the strict equality operator, which consists of three equal signs: ===. It works exactly like the normal equality operator, but without any type coercion. It's recommended to always use the strict equality operator, and explicitly convert types if needs be. As mentioned earlier, this is the default in CoffeeScript, with any weak equality operators being converted into strict ones.
+
The solution is to use the strict equality operator, which consists of three equal signs: ===. It works exactly like the normal equality operator, but without any type coercion. It's recommended to always use the strict equality operator, and explicitly convert types if needs be. As mentioned earlier, this is the default in CoffeeScript, with any weak equality operators being converted into strict ones.
-
+
-
if 10 == "+10" then "type coercion fail"
-
+
if 10 == "+10" then "type coercion fail"
-
String interpolation
+
String interpolation
-
CoffeeScript brings Ruby style string interpolation to JavaScript. Double quotes strings can contain #{} tags, which contain expressions to be interpolated into the string.
+
CoffeeScript brings Ruby style string interpolation to JavaScript. Double quotes strings can contain #{} tags, which contain expressions to be interpolated into the string.
-
+
-
favourite_color = "Blue. No, yel..."
+
favourite_color = "Blue. No, yel..."
question = "Bridgekeeper: What... is your favourite color?
Galahad: #{favourite_color}
Bridgekeeper: Wrong!
- "
-
+ "
-
As you can see in the example above, multiline strings are also allowed, without having to prefix each line with a +:
+
As you can see in the example above, multiline strings are also allowed, without having to prefix each line with a +:
-
Loops and Comprehensions
+
Loops and Comprehensions
-
Array iteration in JavaScript has a rather archaic syntax, reminiscent of an older language like C rather than a modern object orientated one. The introduction of ES5 improved that situation somewhat, with the forEach() function, but that still requires a function call every iteration and is therefore much slower. Again, CoffeeScript comes to the rescue, with a beautiful syntax:
+
Array iteration in JavaScript has a rather archaic syntax, reminiscent of an older language like C rather than a modern object orientated one. The introduction of ES5 improved that situation somewhat, with the forEach() function, but that still requires a function call every iteration and is therefore much slower. Again, CoffeeScript comes to the rescue, with a beautiful syntax:
-
+
-
for name in ["Roger", "Roderick", "Brian"]
- alert "Release #{name}"
-
+
for name in ["Roger", "Roderick", "Brian"]
+ alert "Release #{name}"
-
If you need the current iteration index, just pass an extra argument:
+
If you need the current iteration index, just pass an extra argument:
-
+
-
for name, i in ["Roger the pickpocket", "Roderick the robber"]
- alert "#{i} - Release #{name}"
-
+
for name, i in ["Roger the pickpocket", "Roderick the robber"]
+ alert "#{i} - Release #{name}"
-
You can also iterate on one line, using the postfix form.
+
You can also iterate on one line, using the postfix form.
-
+
-
release prisoner for prisoner in ["Roger", "Roderick", "Brian"]
-
+
release prisoner for prisoner in ["Roger", "Roderick", "Brian"]
-
As with Python comprehensions, you can filter them:
+
As with Python comprehensions, you can filter them:
-
+
-
prisoners = ["Roger", "Roderick", "Brian"]
-release prisoner for prisoner in prisoners when prisoner[0] is "R"
-
+
prisoners = ["Roger", "Roderick", "Brian"]
+ release prisoner for prisoner in prisoners when prisoner[0] is "R"
-
You can also use comprehensions for iterating over properties in objects. Instead of the in keyword, use of.
+
You can also use comprehensions for iterating over properties in objects. Instead of the in keyword, use of.
-
+
-
names = sam: seaborn, donna: moss
-alert("#{first} #{last}") for first, last of names
-
+
names = sam: seaborn, donna: moss
+alert("#{first} #{last}") for first, last of names
-
The only low-level loop that CoffeeScript exposes is the while loop. This has similar behavior to the while loop in pure JavaScript, but has the added advantage that it returns an array of results, i.e. like the Array.prototype.map() function.
+
The only low-level loop that CoffeeScript exposes is the while loop. This has similar behavior to the while loop in pure JavaScript, but has the added advantage that it returns an array of results, i.e. like the Array.prototype.map() function.
-
+
-
num = 6
+
num = 6
minstrel = while num -= 1
- num + " Brave Sir Robin ran away"
-
-
-
Arrays
+ num + " Brave Sir Robin ran away"
-
CoffeeScript takes inspiration from Ruby when it comes to array slicing by using ranges. Ranges are created by two numerical values, the first and last positions in the range, separated by .. or .... If a range isn't prefixed by anything, CoffeeScript expands it out into an array.
+
Arrays
-
+
CoffeeScript takes inspiration from Ruby when it comes to array slicing by using ranges. Ranges are created by two numerical values, the first and last positions in the range, separated by .. or .... If a range isn't prefixed by anything, CoffeeScript expands it out into an array.
-
range = [1..5]
-
+
-
If, however, the range is specified immediately after a variable, CoffeeScript converts it into a slice() method call.
+
range = [1..5]
+
-
+
If, however, the range is specified immediately after a variable, CoffeeScript converts it into a slice() method call.
-
firstTwo = ["one", "two", "three"][0..1]
-
+
-
In the example above, the range returns a new array, containing only the first two elements of the original array. You can also use the same syntax for replacing an array segment with another array.
+
firstTwo = ["one", "two", "three"][0..1]
-
+
In the example above, the range returns a new array, containing only the first two elements of the original array. You can also use the same syntax for replacing an array segment with another array.
-
numbers = [0..9]
-numbers[3..5] = [-3, -4, -5]
-
+
-
What's neat, is that JavaScript allows you to call slice() on strings too, so you can use ranges with string to return a new subset of characters.
+
numbers = [0..9]
+numbers[3..5] = [-3, -4, -5]
-
+
What's neat, is that JavaScript allows you to call slice() on strings too, so you can use ranges with string to return a new subset of characters.
-
my = "my string"[0..2]
-
+
-
Checking to see if a value exists inside an array is always a bore in JavaScript, particular as indexOf() doesn't yet have full cross-browser support (IE, I'm talking about you). CoffeeScript solves this with the in operator, for example.
+
my = "my string"[0..2]
-
+
Checking to see if a value exists inside an array is always a bore in JavaScript, particular as indexOf() doesn't yet have full cross-browser support (IE, I'm talking about you). CoffeeScript solves this with the in operator, for example.
-
words = ["rattled", "roudy", "rebbles", "ranks"]
-alert "Stop wagging me" if "ranks" in words
-
+
-
Aliases & the Existential Operator
+
words = ["rattled", "roudy", "rebbles", "ranks"]
+alert "Stop wagging me" if "ranks" in words
-
CoffeeScript includes some useful aliases to save some typing. One of which is @, which is an alias for this.
+
Aliases & the Existential Operator
-
+
CoffeeScript includes some useful aliases to save some typing. One of which is @, which is an alias for this.
-
@saviour = true
-
+
-
Another is ::, which is an alias for prototype
+
@saviour = true
-
+
Another is ::, which is an alias for prototype
-
User::first = -> @records[0]
-
+
-
Using if for null checks in JavaScript is common, but has a few pitfalls in that empty strings and zero are both coerced into false, which can catch you out. CoffeeScript existential operator ? returns true unless a variable is null or undefined, similar to Ruby's nil?.
+
User::first = -> @records[0]
-
+
Using if for null checks in JavaScript is common, but has a few pitfalls in that empty strings and zero are both coerced into false, which can catch you out. CoffeeScript existential operator ? returns true unless a variable is null or undefined, similar to Ruby's nil?.
-
praise if brian?
-
+
-
You can also use it in place of the || operator:
+
praise if brian?
-
+
You can also use it in place of the || operator:
-
velocity = southern ? 40
-
+
-
If you're using a null check before accessing a property, you can skip that by placing the existential operator right before the opening parens. This is similar to Ruby's try method.
+
velocity = southern ? 40
-
+
If you're using a null check before accessing a property, you can skip that by placing the existential operator right before the opening parens. This is similar to Ruby's try method.
-
blackKnight.getLegs()?.kick()
-
+
-
You can safely call a value in the same manner, checking function-ness beforehand.
+
blackKnight.getLegs()?.kick()
-
+
You can safely call a value in the same manner, checking function-ness beforehand.
-
whiteKnight.guard? us
-
+
-
-
-
+
whiteKnight.guard? us
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/coffeescript/03_classes.html b/coffeescript/03_classes.html
index 4ea38c7..f5220f8 100644
--- a/coffeescript/03_classes.html
+++ b/coffeescript/03_classes.html
@@ -1,131 +1,117 @@
-
-
-The Little Book on CoffeeScript - Classes
-
-
-
-
-
-
-
-
-
-
Classes in JavaScript seem to have the kind of effect that cloves of garlic have to Dracula for some purists; although, let's be honest, if you're that way inclined, you're unlikely to be reading a book on CoffeeScript. However, it turns out that classes are just as damn useful in JavaScript as they are in other languages and CoffeeScript provides a great abstraction.
-
-
Behind the scenes, CoffeeScript is using JavaScript's native prototype to create classes; adding a bit of syntactic sugar for static property inheritance and context persistence. As a developer all that's exposed to you is the class keyword.
-
-
-
-
class Animal
-
-
-
In the example above, Animal is the name of the class, and also the name of the resultant variable that you can use to create instances. Behind the scenes CoffeeScript is using construction functions, which means you can instantiate classes using the new operator.
-
-
-
-
animal = new Animal
-
-
-
Defining constructors (functions that get invoked upon instantiation) is simple, just use a function named constructor. This is akin to using Ruby's initialize or Python's __init__.
-
-
-
-
class Animal
+
+
+ The Little Book on CoffeeScript - Introduction
+
+
+
+
+
+
+
+
Classes in JavaScript seem to have the kind of effect that cloves of garlic have to Dracula for some purists; although, let's be honest, if you're that way inclined, you're unlikely to be reading a book on CoffeeScript. However, it turns out that classes are just as damn useful in JavaScript as they are in other languages and CoffeeScript provides a great abstraction.
+
Behind the scenes, CoffeeScript is using JavaScript's native prototype to create classes; adding a bit of syntactic sugar for static property inheritance and context persistence. As a developer all that's exposed to you is the class keyword.
+
+
+
class Animal
+
+
In the example above, Animal is the name of the class, and also the name of the resultant variable that you can use to create instances. Behind the scenes CoffeeScript is using construction functions, which means you can instantiate classes using the new operator.
+
+
+
+
animal = new Animal
+
+
Defining constructors (functions that get invoked upon instantiation) is simple, just use a function named constructor. This is akin to using Ruby's initialize or Python's __init__.
+
+
+
+
class Animal
constructor: (name) ->
- @name = name
-
+ @name = name
-
In fact, CoffeeScript provides a shorthand for the common pattern of setting instance properties. By prefixing argument's with @, CoffeeScript will automatically set the arguments as instance properties in the constructor. Indeed, this shorthand will also work for normal functions outside classes. The example below is equivalent to the last example, where we set the instance properties manually.
+
In fact, CoffeeScript provides a shorthand for the common pattern of setting instance properties. By prefixing argument's with @, CoffeeScript will automatically set the arguments as instance properties in the constructor. Indeed, this shorthand will also work for normal functions outside classes. The example below is equivalent to the last example, where we set the instance properties manually.
-
+
-
class Animal
- constructor: (@name) ->
-
+
class Animal
+ constructor: (@name) ->
-
As you'd expect, any arguments passed on instantiation are proxied to the constructor function.
+
As you'd expect, any arguments passed on instantiation are proxied to the constructor function.
-
+
-
animal = new Animal("Parrot")
-alert "Animal is a #{animal.name}"
-
+
animal = new Animal("Parrot")
+alert "Animal is a #{animal.name}"
-
Instance properties
+
Instance properties
-
Adding additional instance properties to a class is very straightforward, it's exactly the syntax as adding properties onto an object. Just make sure properties are indented correctly inside the class body.
+
Adding additional instance properties to a class is very straightforward, it's exactly the syntax as adding properties onto an object. Just make sure properties are indented correctly inside the class body.
-
+
-
class Animal
+
class Animal
price: 5
sell: (customer) ->
animal = new Animal
-animal.sell(new Customer)
-
+animal.sell(new Customer)
-
Context changes are rife within JavaScript, and earlier in the Syntax chapter we talked about how CoffeeScript can lock the value of this to a particular context using a fat arrow function: =>. This ensures that whatever context a function is called under, it'll always execute inside the context it was created in. CoffeeScript has extended support for fat arrows to classes, so by using a fat arrow for an instance method you'll ensure that it's invoked in the correct context, and that this is always equal to the current instance.
+
Context changes are rife within JavaScript, and earlier in the Syntax chapter we talked about how CoffeeScript can lock the value of this to a particular context using a fat arrow function: =>. This ensures that whatever context a function is called under, it'll always execute inside the context it was created in. CoffeeScript has extended support for fat arrows to classes, so by using a fat arrow for an instance method you'll ensure that it's invoked in the correct context, and that this is always equal to the current instance.
-
+
-
class Animal
+
class Animal
price: 5
sell: =>
alert "Give me #{@price} shillings!"
animal = new Animal
-$("#sell").click(animal.sell)
-
+$("#sell").click(animal.sell)
-
As demonstrated in the example above, this is especially useful in event callbacks. Normally the sell() function would be invoked in the context of the #sell element. However, by using fat arrows for sell(), we're ensuring the correct context is being maintained, and that this.price equals 5.
+
As demonstrated in the example above, this is especially useful in event callbacks. Normally the sell() function would be invoked in the context of the #sell element. However, by using fat arrows for sell(), we're ensuring the correct context is being maintained, and that this.price equals 5.
-
Static properties
+
Static properties
-
How about defining class (i.e. static) properties? Well, it turns out that inside a class definition, this refers to the class object. In other words you can set class properties by setting them directly on this.
+
How about defining class (i.e. static) properties? Well, it turns out that inside a class definition, this refers to the class object. In other words you can set class properties by setting them directly on this.
-
+
-
class Animal
+
class Animal
this.find = (name) ->
-Animal.find("Parrot")
-
+Animal.find("Parrot")
-
In fact, as you may remember, CoffeeScript aliases this to @, which lets you write static properties even more succinctly:
+
In fact, as you may remember, CoffeeScript aliases this to @, which lets you write static properties even more succinctly:
-
+
-
class Animal
+
class Animal
@find: (name) ->
-Animal.find("Parrot")
-
+Animal.find("Parrot")
-
Inheritance & Super
+
Inheritance & Super
-
It wouldn't be a proper class implementation without some form of inheritance, and CoffeeScript doesn't disappoint. You can inherit from another class by using the extends keyword. In the example below, Parrot extends from Animal, inheriting all of its instance properties, such as alive()
+
It wouldn't be a proper class implementation without some form of inheritance, and CoffeeScript doesn't disappoint. You can inherit from another class by using the extends keyword. In the example below, Parrot extends from Animal, inheriting all of its instance properties, such as alive()
You'll notice that in the example above, we're using the super() keyword. Behind the scenes, this is translated into a function call on the class' parent prototype, invoked in the current context. In this case, it'll be Parrot.__super__.constructor.call(this, "Parrot");. In practice, this will have exactly the same effect as invoking super in Ruby or Python, invoking the overridden inherited function.
+
You'll notice that in the example above, we're using the super() keyword. Behind the scenes, this is translated into a function call on the class' parent prototype, invoked in the current context. In this case, it'll be Parrot.__super__.constructor.call(this, "Parrot");. In practice, this will have exactly the same effect as invoking super in Ruby or Python, invoking the overridden inherited function.
-
Unless you override the constructor, by default CoffeeScript will invoke the parent's constructor when instances are created.
+
Unless you override the constructor, by default CoffeeScript will invoke the parent's constructor when instances are created.
-
CoffeeScript uses prototypal inheritance to automatically inherit all of a class's instance properties. This ensures that classes are dynamic; even if you add properties to a parent class after a child has been created, the property will still be propagated to all of its inherited children.
+
CoffeeScript uses prototypal inheritance to automatically inherit all of a class's instance properties. This ensures that classes are dynamic; even if you add properties to a parent class after a child has been created, the property will still be propagated to all of its inherited children.
-
+
-
class Animal
+
class Animal
constructor: (@name) ->
class Parrot extends Animal
@@ -155,18 +140,17 @@
Inheritance & Super
Animal::rip = true
parrot = new Parrot("Macaw")
-alert("This parrot is no more") if parrot.rip
-
+alert("This parrot is no more") if parrot.rip
-
It's worth pointing out though that static properties are copied to subclasses, rather than inherited using prototype as instance properties are. This is due to implementation details with JavaScript's prototypal architecture, and is a difficult problem to work around.
+
It's worth pointing out though that static properties are copied to subclasses, rather than inherited using prototype as instance properties are. This is due to implementation details with JavaScript's prototypal architecture, and is a difficult problem to work around.
-
Mixins
+
Mixins
-
Mixins are not something supported natively by CoffeeScript, for the good reason that they can be trivially implemented. For example, here's two functions, extend() and include() that'll add class and instance properties respectively to a class.
+
Mixins are not something supported natively by CoffeeScript, for the good reason that they can be trivially implemented. For example, here's two functions, extend() and include() that'll add class and instance properties respectively to a class.
-
+
-
extend = (obj, mixin) ->
+
extend = (obj, mixin) ->
obj[name] = method for name, method of mixin
obj
@@ -177,12 +161,18 @@
Mixins
include Parrot,
isDeceased: true
-(new Parrot).isDeceased
-
-
-
Mixins are a great pattern for sharing common logic between modules when inheritance is not suited. The advantage of mixins, is that you can include multiple ones, compared to inheritance where only one class can be inherited from.
+(new Parrot).isDeceased
+
+
Mixins are a great pattern for sharing common logic between modules when inheritance is not suited. The advantage of mixins, is that you can include multiple ones, compared to inheritance where only one class can be inherited from.