There’s no two ways about it: the web development industry can be incredibly tough. It’s one that requires us to continuously educate ourselves throughout our careers. Take a few years off, and, suddenly, you’ll find yourself buried by new frameworks, tools, best practices, and preprocessors. That’s right, kiddies, if you want to be a web developer, you better be okay with the idea that your schooling will never end (and you shouldn’t want it to).
Once we acknowledge that a certain percentage of every week should be spent fine-tuning our skills and learning new things, determining what, specifically, to invest our time into becomes incredibly important. Seemingly every day, new libraries or tools are released to the public. But this doesn’t mean that we should throw caution (and time) to the wind, and embrace all of them; not at all. Certainly keep a curious mind, but a healthy level of skepticism is paramount.
Inevitably, though, certain tools quickly jump to the top of the “new and shiny” list.
These are the types that have the potential to redefine how we build the web. jQuery redefined the way in which we query and work with the DOM. Backbone.js is helping developers transform their spaghetti code into well structured and maintainable modules. And, just maybe, CoffeeScript will reinvent the way we physically write our JavaScript.
There’s just one problem: CoffeeScript doesn’t add anything new to the JavaScript language; it merely offers an improved (many would say drastically improved) syntax, which is ultimately compiled into regular JavaScript. So our dilemma is: in an environment that already requires us to learn countless languages, techniques, and patterns, is it really worth the time investment to learn yet another preprocessor? Well, while the answer to that question can certainly be debated, in this writer’s opinion, you absolutely should dig in!
Wait, What’s CoffeeScript Again?
It’s possible that this is the first you’ve heard of CoffeeScript. If so, don’t fret; it’s only just now beginning to truly pick up steam across the development community.
At its core, CoffeeScript, created by Jeremy Ashkenas, transforms what some would refer to as “ugly JavaScript” into clean and succinct code. It doesn’t extend the functionality of the JavaScript language; it merely layers a more beautiful syntax.
The best way to demonstrate what CoffeeScript offers is by taking a stroll through the syntax.
Installation
If you simply wish to toy with the syntax, visit CoffeeScript.org, and click on the “Try CoffeeScript” tab. On the other hand, if you want to install CoffeeScript locally, make sure that you have up-to-date copies of both Node.js and npm (Node Package Manager) installed on your system. Don’t worry; it’s easy.
Next, CoffeeScript can be installed from the command line:
npm install -g coffee-script
That’s it! You’re all set to go. To watch a file, script.coffee
, and compile it to script.js
every time the file is saved, within the command line, browse to your project root, and type:
coffee --watch --compile script.coffee
Additionally, many editors, such as TextMate and Sublime Text 2, offer bundles that turn this build process into a simple key stroke. As an alternate option, refer to apps, like LiveReload and CodeKit, which will handle the “watch and compile” task for you automatically.
Variables
Consider the following snippet of code:
var foo = 'bar'; var bar = 'baz';
In CoffeeScript, var
should never be used; in fact, the engine will throw an error if you attempt to do so. Instead, all variables are automatically declared in the current scope - meaning that we no longer have to worry about accidentally creating nasty global variables.
The above code can be rewritten as:
foo = 'bar' bar = 'baz'
When the code is ultimately compiled to regular JavaScript, both variable names will be declared at the top of the function scope, and then assigned accordingly, like so:
var bar, foo; foo = 'bar'; bar = 'baz';
Semicolons
Plenty of JavaScript developers hate the fact that all expressions should end with a semicolon.
var foo = 'bar';
Technically, you’ll find that, frequently, you can get away with leaving off semicolons; but, nonetheless, there are gotchas involved with this approach, and it’s been deemed a best practice to always include them for maximum assurance.
In CoffeeScript, however, we can wave goodbye to them forever. As such, the previous code snippet can be modified to:
foo = 'bar'
Parenthesis
Taking additional cues from languages like Ruby, in CoffeeScript, parenthesis, too, can often be omitted. This transforms traditional JavaScript, such as:
if ( guess === 10 ) { result = 'correct'; }
into:
if guess == 10 result = 'correct'
Even better, we can be more succinct by using a statement modifier:
result = 'correct' if guess == 10
Much cleaner and readable, huh? “Set result
to "correct” if the guess
variable is equal to 10.“ Excellent!
Please note that CoffeeScript compiles all
==
into the strict equality version,===
, as recommended by tools, like JS Lint.
Aliases
You’ll quickly find that, in CoffeeScript, the same block of code can be rewritten in a number of ways. Though you’re free to ignore them, aliases allow for more human readable comparisons. The following groupings are identical in both functionality and compilation.
// equality launch == 'go' launch is 'go' // inequality launch != 'go' launch isnt 'go' // not return false if !goForFlight return false if not goForFlight return false unless goForFlight // true return true return on return yes // false return false return off return no // and goForFlight && launch() goForFlight and launch() // or goForFlight || prepare() goForFlight or prepare()
Functions
Functions are a key area where the syntax is considerably different from JavaScript. A traditional function that determines whether it’s pay day - or Friday - might look like:
var payDay = function() { return new Date().getDay() === 5; // is it friday? }
In an attempt to clean up the clutter, with CoffeeScript, this function can be rewritten as:
payDay = -> new Date().getDay() == 5
Alternatively, we can place the entire bit of code on a single line.
payDay = -> new Date().getDay() == 5
There’s two key things worth noting here:
-
function()
has been replaced with->
. - The last line of a function will always be returned. As such, the
return
keyword may be removed in this particular case.
Any applicable arguments should be placed within parenthesis, before the ->
. Perhaps pay day is on Thursday; if so, the function can be modified to be more flexible.
payDay = (day) -> new Date().getDay() == day
But, what if we want to assign a default pay day of Friday (or 5)? In regular JavaScript, we’d likely do:
var payDay = function(day) { day || ( day = 5 ); return new Date().getDay() === day; }
With CoffeeScript, on the other hand, we can shorten this greatly.
payDay = (day = 5) -> new Date().getDay() == day
Nifty!
Objects
A typical JavaScript object can be cleaned up considerably in CoffeeScript. Consider the following:
var person = { legs: 2, hands: 2, fingers: 10 }
CoffeeScript allows us to remove the var
keyword, as well as both the curly braces and commas. On a single line, though, commas continue to be required.
person = legs: 2, hands: 2, fingers: 10
However, if we place each property on its own line, they may be omitted. The same is true for arrays.
person = legs: 2 hands: 2 fingers: 10
Important: because CoffeeScript is white-space dependent, even a single incorrectly indented line can drastically change the way in which the code is compiled to JavaScript.
Comprehension
Comprehension allows us to easily transform ten lines of code into just a few. It’s essentially a way to create expressions to iterate over a provided set of items in an array or object.
For instance, rather than using the typical for
statement - and the “caching” that goes along with iterating over an array - we can instead use for in
.
Let’s imagine that we need to iterate over an array of characters. With traditional JavaScript, we might do:
var characters = ['Marty', 'Doc', 'Biff']; for ( var i = 0, len = characters.length; i < len; i++ ) { console.log("Get %s to the time machine", characters[i]); }
It’s certainly not pretty; but, if we instead use CoffeeScript, it can be beautiful.
characters = ['Marty', 'Doc', 'Biff'] for person in characters console.log "Get %s to the time machine", person
Doesn’t that feel great to write? Surely, I can’t be the only one!
As you’ll find shortly, we can even use string interpolation to improve the code further - but I’m getting ahead of myself.
There’s a problem, though: we’ve inadvertently directed Biff to the time machine, which is not a good idea. Let’s modify the code to specify that it should only log the string to the console on the condition that the current person in the array is not Biff
. In CoffeeScript, if we take advantage of filters, that’s a cinch.
characters = ['Marty', 'Doc', 'Biff'] for person in characters when person isnt 'Biff' console.log "Get %s to the time machine", person
Yep; the remedy required a total of four human readable words.
Now, if we wish, we can make these loops more readable by using list comprehension. To loop through the characters
array, and log each person’s name to the console:
console.log person for person in characters
Or, as another example, to retrieve all anchor tags from the page (using jQuery’s map
method), store each anchor’s href
value within an array, and finally loop through that array, and log the values to the console, with regular JavaScript, we’d do:
var links = $('a').map(function() { return this.href; }); console.log(links.join(', '));
With CoffeeScript, however, we have better choices:
console.log ( link for link in $('a').map -> @href ).join ', '
As a final example, what if we had an array of person objects:
people = [ name: 'Jeffrey' age: 27 , name: 'John' age: 13 , name: 'Jan' age: 42 ]
Our job is to create a new array, called ofAge
, and make it equal to a list of only the objects, where the person’s age is 21 years or older. Again, with regular, vanilla JavaScript, we might do:
var ofAge = []; for ( var i = 0, len = people.length; i < len; i++ ) { if ( people[i].age >= 21 ) { ofAge.push( people[i] ); } }
Yep, it’s fairly verbose for such a simple task. With CoffeeScript, let’s reduce this to a single line, using comprehension.
ofAge = ( p for p in people when p.age >= 21 )
Bam! By wrapping everything that occurs after the equal sign within parenthesis, we specify that the ofAge
variable should be equal to the results of that operation, which will be an array. So the array will be built, and then assigned to ofAge
. When confused, simply read the expression from left to right. “Push the person object to the results
array, for each person
in the people
array…just as long as the person’s age
property is greater than or equal to 21.” The when
keyword is called a filter, and can be incredibly powerful. Admit it: you’re beginning to salivate.
String Interpolation
In the previous section, we used a fairly typical method to bind a variable to a string.
console.log "Get %person to the time machine", person
Similar to languages, like Ruby, CoffeeScript offers string interpolation, via the #{}
syntax. This code can be rewritten, like so:
console.log "Get #{person} to the time machine"
Please note that, like most languages, to take advantage of string interpolation, you must use double quotes, rather than single.
Function Binding
Consider the fairly common task of caching a reference to this
, so that, when the context changes - such as within the callback of a jQuery event binding - we can still access the cached location. Here’s an example:
var self = this; $('h1').on('click', function() { // 'this' now refers to the anchor that was clicked // use self instead self.someMethod(); });
CoffeeScript provides the “fat arrow”, or =>
, which can be of tremendous help. If we change ->
to =>
, behind the scenes, CoffeeScript will cache a reference to this
. Then, within the function, all references to this
will dynamically be replaced with the cached version.
To illustrate this idea, the following code:
$('h1').on 'click', => this.someMethod()
…will compile to:
var _this = this; $('h1').on('click', function() { return _this.someMethod(); });
Classes
CoffeeScript provides a helpful class
syntax, for those who prefer a more classical approach to structuring their code. For instance, by simply typing class Person
, CoffeeScript will compile that code to:
var Person; Person = (function() { function Person() {} return Person; })();
Notice how the Person
variable is equal to a self invoking function expression that returns the inner Person
function. Should we need to execute a bit of code immediately upon instantiation, similar to PHP’s __construct
method, we can place our init
code within the constructor
method of the class
, like so:
class Person constructor: (name, age) -> this.name = name this.age = age
The constructor
method can’t be called explicitly; instead, it’s triggered dynamically when you instantiate the class. This code can be improved, however; CoffeeScript provides some extra sugar that can shorten it. In CoffeeScript, the @
symbol will always refer to this
, or the instance of Person
. As such, rather than this.age
, we can instead use @age
, which Ruby developers will be familiar with. The code above may be rewritten as:
class Person constructor: (name, age) -> @name = name @age = age
Even better, though, we can take things one step further. These instance variables may be set in a different way:
class Person constructor: (@name, @age) ->
Not bad, huh? At this point, to extend Person
’s prototype with additional methods, we simply create new methods, in the same way that we would add methods to an object. Behind the scenes, CoffeeScript will attach the method to the prototype. Let’s add a method that determines the person’s birth year.
class Person constructor: (@name, @age) -> getBirthYear: -> new Date().getFullYear() - @age
This beautiful, clean code, once compiled to JavaScript, will be:
var Person; Person = (function() { function Person(name, age) { this.name = name; this.age = age; } Person.prototype.getBirthYear = function() { return new Date().getFullYear() - this.age; }; return Person; })();
So, we’ve effectively cut the number of lines from sixteen down to four. To instantiate this new class, we can write:
man = new Person('Jeffrey', 27) man.getBirthYear() # 1985
Even better, to create child classes, which extend Person
, we only need to use the extend
keyword:
class Child extends Person
At this point, Child
now has access to all of the properties and methods from the Person
class, and can reference or overwrite them at will.
Should You Use CoffeeScript?
Despite all of this glorious sugar, we’re still left with the question: should we use CoffeeScript in production? Well, John Q. Reader, that’s largely up to you. There are valid arguments for and against it. The nay-sayers will point to the fact that, because you’re not working specifically with the compiled JavaScript, debugging can potentially become a more difficult task. You’re debugging code that you didn’t write. That can be a struggle.
“I think CoffeeScript is a brilliant experiment. CoffeeScript takes the good parts, and puts a minimal syntax on top of it, which is lovely. I don’t recommend using it in production, because it comes with its own world of problems. - Douglas Crockford”
One could also argue that too much dependency on abstractions like this can result in the developer having a lesser understanding of JavaScript, as a whole. Because so many best practices are baked into the compiler, it eases the burden on the developer to memorize these best practices, such as avoiding globals, and hoisting variables. Whether or not this sort of automation is a good thing is up for debate.
“A best practice that can be enforced and generated by a compiler is better than a best practice that has to be remembered and manually typed out every time.” - Jeremy Ashkenas
To play devil’s advocate, the same could have been said for jQuery. Does jQuery encourage a new breed of developer who never truly learns vanilla JavaScript? Absolutely not; if anything, it has assisted greatly in the resurgence of the JavaScript language.
While the CoffeeScript syntax may initially be more familiar and welcoming to Ruby and Python developers who are afraid of JavaScript, hopefully, it will encourage them to dig more deeply into the underlying language.
Summary
Ultimately, CoffeeScript is just JavaScript. It offers a beautiful syntax that sits on top of the language. The pros are obvious: code automatically adheres to best practices, and becomes shorter, less error-prone, and considerably more readable. But, then again, debugging, which can take up a significant amount of time in a developer’s workflow, is a concern.
The choice is up to you! As for yours truly, well I can’t imagine going back. I’m all in with CoffeeScript.
Further Learning
- Cleaner Code With CoffeeScript (Coming Soon to Tuts+ Premium!)
- Nettuts+: Should You Learn CoffeeScript?
- CodeSchool: A Sip of CoffeeScript
- Pragmatic Bookshelf: CoffeeScript - Accelerated JavaScript Development
Comments