Understanding Fat Arrows (=>) in CoffeeScript

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:

Fat Arrows with Closure in CoffeeScript

Fat Arrows with Closure in CoffeeScript

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:

Fat Arrows with Classes in CoffeeScript

Fat Arrows with Classes in CoffeeScript

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:

Fat Arrows Protect Methods

Fat Arrows Protect Methods

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(@)):

Sidenote: If you like this post and interested in a corporate on-site JavaScript, Node.js and React.js training to boost productivity of your team, then contact NodeProgram.com.

Fat Arrows Method Hasn't Changed

Fat Arrows Method Hasn’t Changed

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:

Fat Arrows with Functions in CoffeeScript

Fat Arrows with Functions in CoffeeScript

The CoffeeScript code:

[Sidenote]

Reading blog posts is good, but watching video courses is even better because they are more engaging.

A lot of developers complained that there is a lack of affordable quality video material on Node. It's distracting to watch to YouTube videos and insane to pay $500 for a Node video course!

Go check out Node University which has FREE video courses on Node: node.university.

[End of sidenote]

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.

Node University: Courses on Node.js, React and JavaScript

Become an expert with my comprehensive Node.js, React.js and JavaScript courses

Learn Full Stack JavaScript

--
Azat Mardan
Azat Mardan avatar
https://www.linkedin.com/in/azatm
To contact Azat, the main author of this blog, submit the contact form.

Also, make sure to get 3 amazing resources to FREE when you sign up for the newsletter.
Simple.
Easy.
No commitment.

3 thoughts on “Understanding Fat Arrows (=>) in CoffeeScript

  1. Pingback: CoffeeScript Fat Arrow (=>) explained | Missing Link

  2. Azat Post author

    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.

  3. Ben

    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?

Leave a Reply

Your email address will not be published. Required fields are marked *