In-Depth
Top 7 Reasons To Love CoffeeScript
Learn why more and more C# developers are turning to CoffeeScript to build their JavaScript-based Web applications.
In my past two articles, ("Moving from Partial Views to AJAX Calls" and "JavaScript Data Binding with Knockout.js"), I've demonstrated some techniques to make it easier to develop Web-based applications. Today, I'm going to look at how using a language called CoffeeScript can help youwrite better JavaScript. One of the nice things about CoffeeScript is that, in many ways, it resembles C# and can make the transition to Web development easier for those not well-versed in JavaScript.
Not Replacing JavaScript
When discussing CoffeeScript, you need to keep in mind that it is not a replacement for JavaScript. CoffeeScript is a "JavaScript generator." Your browser doesn't need any new plug-ins or add-ons to utilize CoffeeScript. Instead, your development environment takes your CoffeeScript and generates browser-ready JavaScript. This also means you can use all your favorite JavaScript libraries (jQuery, Knockout.js, Sammy.js and so on) with CoffeeScript (and vice versa).
You might wonder why I'm not using TypeScript. I got interested in CoffeeScript many months ago, while TypeScript was still in beta (and, as of this writing, it's still in beta). I will definitely take a look at TypeScript, but I really like the brevity and clean flow that CoffeeScript gives me.
Using CoffeeScript Inside Visual Studio
CoffeeScript files, like JavaScript files, are simply files that can be edited in any text editor. There are command-line compilers for CoffeeScript as well as a node.js package that can be installed with a simple "npm install coffee-script." But for me, a C# developer who uses Visual Studio 2012 for his day-to-day coding, I wanted something that worked inside Visual Studio.
Mindscape, a tool and component developer out of New Zealand, has a free extension for Visual Studio 2012 (as well as 2010 and 2013) called Web Workbench that enables editing of CoffeeScript files right inside Visual Studio (as well as Sass and Less files). Just create a new .coffee file, edit away and, when you click "Save," the accompanying JavaScript file is generated and saved as well. You can reference the .js file just as you would any other JavaScript file -- a <script> tag or perhaps even a Bundle. It's just JavaScript.
Now that you know how to work with CoffeScript, here are some of the top reasons developers are gravitating to it:
Reason #1: Lambda-Like Functions
When it comes to composing function calls, JavaScript can be quite verbose. For example, suppose I have a JavaScript array of employee objects. Each object exposes a "role" property, which tells me what role each employee has. If I wanted to get all employees that have a role of "manager," I could use the jQuery grep function. The grep function takes a JavaScript function that's a predicate for deciding whether an element will be returned. Here's how I'd write this in JavaScript:
var managers = $.grep(employees, function (e) {
return e.role == "manager";
});
For those who remember the pre-delegate days of the Microsoft .NET Framework, this looks a lot like an anonymous delegate (which, basically, it is). Lambdas in C# introduced a much nicer syntax for declaring an anonymous delegate; CoffeeScript mimics this syntax. The same function in CoffeeScript could be written as:
managers = $.grep(employees, (e) -> e.role == "manager")
Note the similarities to the C# lambda syntax:
- Arguments to the function are enclosed in parentheses.
- The lambda operator of ->.
- No need to use the "return" keyword; it's implied.
As noted in similarity No. 3, the "return" keyword isn't required. In CoffeeScript functions the last line executed is always considered the return value. For example, I can create a function to calculate the area of a rectangle:
area = (l,w) -> l * w
CoffeeScript compiles this to:
var area;
area = function(l, w) {
return l * w;
};
Note the brevity of CoffeeScript: clean, simple and to the point.
What about multi-line functions? CoffeeScript resembles Python in that it doesn't use curly braces to define code blocks; instead, it uses indentation. If my area calculation was more complex, it might look something like this:
area = (l,w) ->
validateLength l
validateWidth w
l * w
Important points:
- No curly braces. Keeping the same indentation level keeps the code in the same block.
- No need for parentheses when calling a function with parameters (if your function doesn't take parameters, you'll need to add the parentheses to actually call the function).
- No need to use the "return" keyword.
Here's the JavaScript code as generated by CoffeeScript:
var area;
area = function(l, w) {
validateLength(l);
validateWidth(w);
return l * w;
};
CoffeeScript also supports default values for function arguments:
calculatePay = (hours, rate, m = 1) -> hours * rate * m
regularTime = calculatePay 40, 10
doubleTime = calculatePay 40, 10, 2
I'll let the CoffeeScript compiler deal with assigning a default value for "m" if I don't supply one:
var calculatePay, doubleTime, regularTime;
calculatePay = function(hours, rate, m) {
if (m == null) {
m = 1;
}
return hours * rate * m;
};
regularTime = calculatePay(40, 10);
doubleTime = calculatePay(40, 10, 2);
Reason #2: Comparison Operators
If you've used JavaScript enough, you've probably been bit by the == operator and its "under-the-covers" type coercion (or, worse yet, you have done a lot of JavaScript and just haven't noticed it yet). If you do a Web search on "javascript == operator" you'll find plenty of explanations and history behind the confusing "==" and "===" operators in JavaScript (as well as their counterpart "!=" and "!==" operators).
To make things less confusing, CoffeeScript will always use the === and !== operators when generating CoffeeScript that uses the == and != operators. The JavaScript === and !== operators behave the way most other languages do when using the == and != operators. This makes your CoffeeScript code safer and more predictable when it's compiled down to JavaScript.
Other things that can make CoffeeScript comparisons a little easier to read are the built-in aliases for some of the operators and Boolean conditions:
- "yes" and "on" are aliases for true
- "no" and "off" are aliases for false
- "is" is an alias for the == operator (which, remember, compiles to the JavaScript ===)
- "isnt" is an alias for the != operator (which compiles to the JavaScript !==)
This means I can write code like this:
if logging is on showAjaxResponse()
if neverShowAlerts is yes
console.log "skipping alert"
To be honest, I haven't adopted these often. I think I'm just an old guy who's somewhat set in his ways and Boolean conditions with "yes," "no," "on" and "off" look odd to me. I can certainly read other people's code written that way, but it's difficult for me to write it that way. It just doesn't flow from my fingers as easily.
Reason #3: Existential Operator
Surprisingly, I've known a lot of C# programmers that aren't familiar with "??," the null-coalescing operator. In C#, I can write code like this:
var option = setting ?? true;
This says the option variable should be set to setting unless setting is null. In that case, set option to true. It's a shorter way to write:
var option = setting != null ? setting : true;
CoffeeScript has the ? operator, which is called the existential operator. It acts like the C# ?? operator, and takes the complexity out of checking if a variable exists. For example, here's some CoffeeScript code:
angle = selectedAngle ? 90
If selectedAngle has a defined value, "angle" will get that value. Otherwise, angle is set to 90. The same code compiled to JavaScript produces:
angle = typeof selectedAngle !== "undefined" && selectedAngle !== null ? selectedAngle : 90;
I like the CoffeeScript version myself.
CoffeeScript also has a member accessor version of the ? operator: The ?. operator can be used to access a property of a function only if it isn't null. This lets me skip a bunch of null checks on object members and properties:
zipCode = employee?.primaryAddress?.zipCode ? "90026"
This simply reads "if the employee object is non-null and has a non-null primaryAddress property, get the zipCode from that primaryAddress. Otherwise, use 90026." Compile this into JavaScript and you can really appreciate the ?. operator in CoffeeScript:
var zipCode, _ref, _ref1;
zipCode = (_ref = typeof employee !== "undefined" && employee !== null ? (_ref1 = employee.primaryAddress) != null ? _ref1.zipCode : void 0 : void 0) != null ? _ref : "90026";
Reason #4: Tuples
C# 4.0 introduced the Tuple. A Tuple is a data structure that has a fixed number of elements. Tuples are handy when a method needs to return more than a single value. In CoffeeScript, a similar concept for handling multiple return values is implemented using the "destructuring assignment" syntax. Here's a CoffeeScript example:
writerData = ->
["patrick steele", 74, "keith ward" ]
[name, height, editor] = writerData()
Please note that like C#, if a function in CoffeeScript takes no parameters, I don't need to use an empty parameter list ("()"), I can just use the -> operator and start writing my function.
In this example, the variables name, height and editor would be assigned "patrick steele," 74 and "keith ward," respectively. Compiling this to JavaScript produces:
var editor, height, name, writerData, _ref;
writerData = function() {
return ["patrick steele", 74, "keith ward"];
};
_ref = writerData(), name = _ref[0], height = _ref[1], editor = _ref[2];
Again, CoffeeScript provides a simple, elegant syntax for a complex JavaScript operation.
Reason #5: Switch Statements
In C#, to prevent accidental fall-through of a switch/case statement, the compiler requires a break statement at the end of each case block. JavaScript has no requirement and, therefore, accidental fall-throughs are possible.
To prevent this, the CoffeeScript switch/when/else construct automatically inserts a break at the end of each case block when it compiles it to a JavaScript switch/case. The switch statement in CoffeeScript is written "switch [Condition] when [clauses] else [default clause]". Here's a simple example:
dayOfWeek = "sun"
switch dayOfWeek
when "sun"
bedtime = "10pm"
when "mon", "tue", "wed", "thu"
bedtime = "11pm"
when "fri"
bedtime = "1am"
else
bedtime = "2am"
When compiled to JavaScript, the code looks like this:
var bedtime, dayOfWeek;
dayOfWeek = "sun";
switch (dayOfWeek) {
case "sun":
bedtime = "10pm";
break;
case "mon":
case "tue":
case "wed":
case "thu":
bedtime = "11pm";
break;
case "fri":
bedtime = "1am";
break;
default:
bedtime = "2am";
}
Notice the break statements automatically inserted to prevent fall-through. CoffeeScript can also convert a switch statement into an assignable expression. The same switch statement could be written even shorter in CoffeeScript:
dayOfWeek = "sun"
bedtime = switch dayOfWeek
when "sun" then "10pm"
when "mon", "tue", "wed", "thu" then "11pm"
when "fri" then "1am"
else "2am"
And the resulting JavaScript:
var bedtime, dayOfWeek;
dayOfWeek = "sun";
bedtime = (function() {
switch (dayOfWeek) {
case "sun":
return "10pm";
case "mon":
case "tue":
case "wed":
case "thu":
return "11pm";
case "fri":
return "1am";
default:
return "2am";
}
})();
Reason #6: Classes
I left this section for now because JavaScript, as a prototype-based language, doesn't have classes natively and, therefore, doesn't have inheritance. With so many programmers coming to JavaScript with a class-based background in object-oriented programming (OOP), there are a number of JavaScript libraries to help you create a class-like structure in your JavaScript code. But there are complexities in accessing the base class ("super"), and setting up the inheritance chain through the prototype object is confusing and error-prone.
That's why it's so nice that CoffeeScript offers first-class support for defining classes. All of the complexities of JavaScript classes and inheritance are handled by CoffeeScript when the JavaScript is generated.
Classes in CoffeeScript support defined constructors:
class Employee
constructor: (name, type) ->
@name = name
@type = type
The @ prefix on the name variable inside the constructor automatically binds that variable to the "this" context -- basically, it's creating a property on the new class. In fact, creating properties to store constructor arguments is so common that CoffeeScript lets you use a short-hand syntax. The following code is equivalent to the previous code:
class Employee
constructor: (@name, @type) ->
Sub-classing is done using the "extends" keyword. If you don't define a constructor for a sub-class, the base class constructor is automatically called (just like C#). If you do define a constructor for a sub-class, via a call to "super" you can call the base class constructor:
class Manager extends Employee
constructor: (name) ->
super name, 'Manager'
Now I can create a manager:
sue = new Manager 'Sue'
I've shown five lines of code: two to create a base Employee class and three to define a derived Manager class. Those five lines of code get generated into 25 lines of JavaScript, as shown in Listing 1.
Listing 1: 25 Lines of JavaScript Equivalent to Five Lines of CoffeeScript
var Employee, Manager,
__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; };
Employee = (function() {
function Employee(name, type) {
this.name = name;
this.type = type;
}
return Employee;
})();
Manager = (function(_super) {
__extends(Manager, _super);
function Manager(name) {
Manager.__super__.constructor.call(this, name, 'Manager');
}
return Manager;
})(Employee);
As you can see, CoffeeScript handles a lot of the dirty work involved in getting proper inheritance into JavaScript.
As shown in the constructor sample, the super keyword can also be used to access a base class member when overriding methods in a subclass, as shown in Listing 2.
Listing 2: Using the Super Keyword to Access a Base Class Member in CoffeeScript
class Cat
constructor: (@name) ->
jump: (distance) ->
alert "#{@name} jumped #{distance}"
class HouseCat extends Cat
jump: ->
super "3 feet"
class Cougar extends Cat
jump: ->
super "20 feet"
morris = new HouseCat "morris"
morris.jump()
killer = new Cougar "KILLER"
killer.jump()
For the sake of brevity, I'm going to omit the 55-plus lines of JavaScript code into which the CoffeeScript code in Listing 2 compiles. Also, note the special #{} syntax used in the jump method. This is a quick way of doing Ruby-like string interpolation. The value of the variable enclosed inside the "#{}" braces is evaluated and placed within the string.
Reason #7: Function Binding
Earlier, when talking about class constructors, I mentioned how CoffeeScript allows me to use the "@" prefix on a variable to bind the variable to the current "this" context (the object being created), thus creating a property on the new object.
Function binding has a similar shortcut. When defining a function I need to make sure is scoped (or "bound") to the current "this" context, I can use => ("fat arrow") instead of the regular arrow, ->.
The complexities of the "this" keyword in JavaScript are well-documented. Here's a simple example. I'll start by creating a new function that will serve as a creator for a Person object:
function Person() {
this.firstName = 'Patrick';
this.lastName = 'Steele';
this.fullName = function() {
return this.firstName + ' ' + this.lastName;
}
}
Using this object is easy:
var person = new Person();
console.log(person.fullName());
"Patrick Steele" is the output.
Now, suppose I create some other function to create a Logger object. This object has a function that accepts a function, executes that function and logs the results:
function Logger() {
this.show = function(namefn) {
console.log(namefn());
}
}
Now instead of calling console.log directly, I'll just pass my person object fullName function to the logger:
var logger = new Logger();
logger.show(person.fullName);
When I used the fullName function earlier, "Patrick Steele" was the output. Now I'm taking that same function and passing it somewhere else to be executed. But in doing that, I've changed the context under which the function is running. So when "this.firstName" and "this.lastName" are evaluated, the context of "this" is the Logger object. Guess what happens? My output is now "undefined undefined."
There's an easy fix for this. At the top of my JavaScript class, I can get a reference to the current "this" context, hold on to it and use it when I need to have my functions bound to the current "this" context:
function Person() {
var self = this;
this.firstName = 'Patrick';
this.lastName = 'Steele';
this.fullName = function() {
return self.firstName + ' ' + self.lastName;
}
}
A simple fix to be sure, but a big "gotcha!" if you aren't aware of this. Fortunately, CoffeeScript is aware of how messy this can be, and allows me to bind my functions to the current "this" context with the fat arrow (=>). The JavaScript person class could be written in CoffeeScript as:
class Person
constructor: (@firstName = 'Patrick', @lastName = 'Steele') ->
fullName: => @firstName + ' ' + @lastName
The fullName function is defined with the fat arrow and will be bound to the context of each Person object created.
What's Your Take?
As you can see, CoffeeScript has a number of similarities to C#, while smoothing out some of the rough edges of JavaScript. I've used it in a number of projects and I've been pleased at the reduction in code while at the same time having more readable code. Try it out and let me know what you think in the comment section.