The fat arrows (=>
) are special function expressions that confuse even the smartest of developers. Most JavaScript professionals know that closure changes the meaning of this
(@
in CoffeeScript).
Fat Arrows with Closures in CoffeeScript
For example, we have a class inside of which @
resolves to itself, but in a nested closure (a.k.a. anonymous function definition) the @
is window
:
The CoffeeScript code:
class A
a: ()->
console.log(@)
b=()-> console.log(@)
b()
a = new A
a.a()
The JavaScript code:
var A, a;
A = (function() {
function A() {}
A.prototype.a = function() {
var b;
console.log(this);
b = function() {
return console.log(this);
};
return b();
};
return A;
})();
a = new A;
a.a();
Try it yourself at the CoffeeScript website.
This is the most common use of fat arrows in CoffeeScript. This case also includes omnipresent jQuery event handlers, clicks, mousedowns, mouseups, etc.
Fat Arrows with Classes in CoffeeScript
Things get a little tricky in the compiled JavaScript code when we try to apply =>
on the objects/classes. However, the results are the same in the console with both logs outputting the same object for this
:
The CoffeeScript code:
class A
a: ()->
console.log(@)
b=()-> console.log(@)
b()
c: ()=>
console.log(@)
a = new A
a.a()
a.c()
The JavaScript code:
var A, a,
__bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
A = (function() {
function A() {
this.c = __bind(this.c, this);
}
A.prototype.a = function() {
var b;
console.log(this);
b = function() {
return console.log(this);
};
return b();
};
A.prototype.c = function() {
return console.log(this);
};
return A;
})();
a = new A;
a.a();
a.c();
Try it yourself at the CoffeeScript website.
What is the difference then? Should we just use fat arrows on object methods instead of skinny ones? The matter become clearer if we expand the object in the console, and observe that it has two method c()
! One in its prototype and another in the instance:
The difference is subtle when we invoke the methods, but remember that JavaScript is a prototypal language. If can augment the parent class and all instances that were extended from that class follow the change of prototype. So if we augment A.a()
and A.c()
methods after we create the a
object, the a.a()
will be updated while a.c()
will stay the same (console.log(@)
):
Fat Arrows with Functions in CoffeeScript
Another usage of fat arrows involves passing of functions to another objects. In this case the ->
method loses the reference to the class/object in which it was originally written while the =>
method keeps track of it even in a foreign context:
The CoffeeScript code:
class A
x: ()->
console.log(@)
y: ()=>
console.log(@)
f = (callback)->
callback()
a = new A
a.x()
a.y()
f(a.x)
f(a.y)
The JavaScript code:
var A, a, f,
__bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
A = (function() {
function A() {
this.y = __bind(this.y, this);
}
A.prototype.x = function() {
return console.log(this);
};
A.prototype.y = function() {
return console.log(this);
};
return A;
})();
f = function(callback) {
return callback();
};
a = new A;
a.x();
a.y();
f(a.x);
f(a.y);
Try it yourself at the CoffeeScript website.
Conclusion about Fat Arrows in CoffeeScript
There’s no reason to be afraid of fat arrows in CoffeeScript. They are time savers (and clarity achievers) when it comes to the nested functions and jQuery event handlers. When they are applied to class methods, the good rule of thumb is: use =>
when we need @
to be the object in which method is written; use->
when we need @
to be the object in which method is executed.
Hi Ben,
I think your explanation is also right. Let me clarify that in my example I used => for y method and it returned the same object in closure while -> method (x) returned window. Therefore, y “remembered” original class/object while x lost it to window.
Hey Azat,
Do you have the last paragraph backwards?
“use => when we need @ to be the object in which method is written; use-> when we need @ to be the object in which method is executed.” — I think you mean the other way around? Skinny arrow (no scope override) implicitly scopes to the “object in which the method is written,” but fat arrow (overrides ‘this’) enforces that a function defined inside a class method is scoped to the “object in which method is executed.” Or do I misunderstand your example?