Arrow Functions and this

Concise function syntax, implicit returns, and how arrow functions change what this means.

An arrow function is a shorter way to write a function expression, which Functions and Parameters covered. It also treats this differently, and that is the real difference.

The shorthands

An arrow function drops the function keyword. The parameters come first, then =>, then the body.

const andor = { title: "Andor", year: 2022 };

// The long form: braces and an explicit return.
const titleLong = (show) => {
  return show.title;
};

// One expression: the braces and the return can both go.
const titleShort = (show) => show.title;

// One parameter: the parentheses can go too.
const titleShortest = show => show.title;

// Braces make a body, and this body has no return.
const titleOops = (show) => { show.title; };

console.log("long form:", titleLong(andor));
console.log("short:    ", titleShort(andor));
console.log("shortest: ", titleShortest(andor));
console.log("no return:", titleOops(andor));

The first three lines all print Andor. The last prints undefined, because braces after => start a body, and that body never returns.

The short form is called an implicit return. You will see it everywhere in React, because so much of React is short functions passed to other code.

Two parameters always need parentheses: (show, kind) => show.title. Only a single parameter may drop them.

Returning an object literal

There is one trap. Braces after => are read as a body, not as an object. To return an object, wrap it in parentheses.

// Braces here are a body, not an object.
const makeShowBroken = (title) => { title: title };

// Parentheses make it an object again.
const makeShow = (title) => ({ title: title });

console.log("without parens:", makeShowBroken("Severance"));
console.log("with parens:   ", makeShow("Severance"));

makeShowBroken prints undefined. JavaScript read title: title as a label, not as a property. makeShow prints the object.

With two properties the broken version does not even run: a body cannot hold title: title, year: 2022. One property hides the mistake, which is what makes it worth knowing.

this is inherited

A plain function gets its own this, decided by how it is called. An arrow function has no this of its own. It uses the this of the place where it was written.

const show = {
  title: "Severance",

  plainInner: function () {
    // A plain function gets its own `this`, not the show.
    const inner = function () {
      if (this === show) {
        return this.title;
      }
      return "not the show";
    };
    return inner();
  },

  arrowInner: function () {
    // The arrow keeps the `this` of arrowInner, which is the show.
    const inner = () => this.title;
    return inner();
  },
};

console.log("plain inner:", show.plainInner());
console.log("arrow inner:", show.arrowInner());

Both inner functions sit in the same place, inside a method of show. The plain one still loses the show, so it prints not the show. The arrow keeps it and prints Severance.

The rule of thumb: arrows for callbacks, function for methods. In React you hand arrows to other code all the time, and an arrow does not care who calls it.

Common mistakes

  • Braces when you meant an object. (title) => { title: title } gives undefined; write (title) => ({ title: title }).
  • A body with no return. Braces after => make a body, so the value has to be returned.
  • An arrow as a method. An arrow has no this of its own, so this.title will not find the object; write methods with function.

Exercise

makeBooster should return a function that adds amount to a rating. Both lines print undefined instead.

Both calls should print:

Severance: 9.7
Andor: 8.5

Hint: look at the braces after the arrow.

function makeBooster(amount) {
  return (rating) => { rating + amount; };
}

const boostByOne = makeBooster(1);

console.log("Severance:", boostByOne(8.7));
console.log("Andor:", boostByOne(7.5));