Like my previous post on the fat arrow in Coffeescript, Coffeescript Fat Arrow: Step-By-Step for this one we are going to slowly step through how classes work in Coffeescript and then introduce how the fat arrow interacts with them so that we can get a complete picture of how this works. You are going to learn this concept the way I did. That means you’re going to learn it step-by-step through observing simple examples of what Coffeescript compiled output looks like.
So, without further delay, here is step one. Let’s see what happens when we just use the ‘class’ keyword in Coffeescript:
class Car
Compiles to this:
(function() {
var Car;
Car = (function() {
function Car() {}
return Car;
})();
}).call(this);
Lets pick this apart. On the first line, we can see the Coffee compiler has created a function which ends on the last line and then is called. This is standard for all compiled Coffeescript, so we can safely ignore the first and last lines. On the second line, we see "var Car;". This is Coffeescript’s way of creating a global variable to identify our class. This isn’t really too different from the way that many programming languages work, it’s just that usually the interpreter in most languages with classes creates a variable to represent the class automatically when the class is defined. With JavaScript, which doesn’t natively support classes, the Coffee compiler has to do this on its own. That way we have a way to refer to the class after we’ve created it.
On the third line (or fourth, if you count that blank line), we can see that a function is being assigned to the ‘Car’ variable. But why is there a parenthesis in front of it? Even weirder, f you look three lines down, we can see that the closing parenthesis after the end of the function’s body and then another set of parentheses immediately after. This is called a "self-executing anonymous function" and is the kind of syntactic garbage that only JavaScript could bring us. Basically, the Coffee compiler is wrapping the entire function that represents our class in a closed scope and then executing it right away. This really serves (as my understanding goes) to keep the class definition from polluting the global namespace. So, in simple terms this mess of parentheses is just part of Coffeescript trying to get as close as possible to classes as they work in other languages.
Also worth noting is that Coffeescript isn’t just creating a self-executing anonymous function. It’s creating a self-executing anonymous function that returns another function. I’m not 100% certain, but I would speculate that this is so that our class can be referred to and called in different ways and still have a consistent response (like "Car" does the same thing as "Car()"). If someone knows more about this, I would love for them to let me know!
Ok, enough of that. Let’s add some stuff to our class. First, I add an attribute to our Car class. Then I create a new instance of the class. Finally, I call a method on the class:
class Car honk: -> "Beep!" honda = new Car alert honda.honk()
That Coffeescript code compiles to this JavaScript:
(function() {
var Car, honda;
Car = (function() {
function Car() {}
Car.prototype.honk = function() {
return "Beep!";
};
return Car;
})();
honda = new Car;
alert(honda.honk());
}).call(this);
What can we learn from this code? Well, we can see that Coffeescript adds methods to classes by adding them to the class’s prototype (or since the class is really a function, the function’s prototype). This is part of the way that Coffeescript uses JavaScript’s prototype-based inheritance goodness to emulate the classes that JavaScript doesn’t really have.
Wonderful. Let’s fatten up our skinny arrow and see what happens:
class Car honk: => "Beep!" honda = new Car alert honda.honk()
And here is the changed JavaScript :
(function() {
var Car, honda,
__bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
Car = (function() {
function Car() {
this.honk = __bind(this.honk, this);
}
Car.prototype.honk = function() {
return "Beep!";
};
return Car;
})();
honda = new Car;
alert(honda.honk());
}).call(this);
Has anything changed? Well, our car still honks. And it’s still a Honda. Now, however, there is a bunch of this weird "__bind" stuff all over.
At the top, we can see if creates a function called "__bind" that takes two arguments. The first argument is a function object, and the second is the current value of "this". With these, the "__bind" function returns a function that calls "apply()" on the provided function and passes the current value of "this". The "apply" function is a built-in JavaScript function that allows us to call a function with a specific value for "this" (the object that owns the function or that the function is a method of). So, really all we need to know about what Coffeescript is doing here is this: Coffeescript takes our function and makes it into a function that always executes with "this" being what "this" meant inside our class definition.
I must admit, it was kind of hard for me to come up with a generic example for the fat arrow in a class that showed its usefulness without being too complicated or contrived. In fact, I didn’t succeed at all. But here is as close as I could get:
class Car
constructor: (@wheels,@doors) ->
features: -> "I have #{@wheels} wheels and #{@doors} doors."
honda = new Car(4,2)
functionThatTakesCallback = (callback) -> callback()
alert functionThatTakesCallback(honda.features)
When this code is run, it alerts: “I have undefined wheels and undefined doors." Why? Well, that is because when we call the "features" function, the value of "this" is the function object that we passed the "honda.features" function to, not the current instance of the "Car" class, "honda."
Now, if we use a fat arrow:
class Car
constructor: (@wheels,@doors) ->
features: => "I have #{@wheels} wheels and #{@doors} doors."
honda = new Car(4,2)
functionThatTakesCallback = (callback) -> callback()
alert functionThatTakesCallback(honda.features)
Our page will alert: "I have 4 wheels and 2 doors." This is because the compiled JavaScript was like this:
(function() {
var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
$(function() {
var Car, functionThatTakesCallback, honda;
Car = (function() {
function Car(wheels, doors) {
this.wheels = wheels;
this.doors = doors;
this.features = __bind(this.features, this);
}
Car.prototype.features = function() {
return "I have " + this.wheels + " wheels and " + this.doors + " doors.";
};
return Car;
})();
honda = new Car(4, 2);
functionThatTakesCallback = function(callback) {
return callback();
};
return alert(functionThatTakesCallback(honda.features));
});
}).call(this);
And in the code, our features function was bound to the value of "this" as it was in the class definition.
That example seems contrived, so I will tell you this. The common practical use for the fat arrow is demonstrated well in The Little Book on CoffeeScript chapter on classes. Their example looks like this:
class Animal
price: 5
sell: =>
alert "Give me #{@price} shillings!"
animal = new Animal
$("#sell").click(animal.sell)
So, you can see that a common practical use for the fat arrow is when you expect to pass your class method (function) to JQuery to use as a callback. Then, we can make sure that we are asking the value of "price" on the animal object, and not on the "sell" HTML element.