Arrow Functions and this

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

What you'll learn

  • The shorthand forms of an arrow function, and when each is readable
  • Why returning an object literal needs extra parentheses
  • How arrow functions inherit this instead of defining their own
  • Where each style belongs in React code

The shorthands

Arrow functions started as a shorter way to write a function expression. Each of these is the same function:

const doubleA = function (n) {
  return n * 2;
};

const doubleB = (n) => {
  return n * 2;
};

// One expression: the braces and the return can both go.
const doubleC = (n) => n * 2;

console.log(doubleA(4), doubleB(4), doubleC(4));

That last form — the implicit return — is the one you will see most in React, because so much of React is short functions passed to other functions.

Returning an object literal

There is one syntax trap. Braces after the arrow are read as a function body, not an object. To return an object literal, wrap it in parentheses:

const wrong = (name) => { name: name };
const right = (name) => ({ name: name });

console.log("without parens:", wrong("Ada"));
console.log("with parens:   ", right("Ada"));

wrong returns undefined: JavaScript reads the braces as a body containing a label, not an object.

this is inherited, not bound

This is the real difference, and the reason arrow functions exist.

A regular 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 scope where it was written. That scope is fixed when you write the code, so it cannot be changed by the caller.

The classic failure is a callback inside a method:

const counter = {
  count: 0,
  label: "clicks",

  brokenReport: function () {
    // A plain function gets its own `this`, which is not the object.
    [1, 2].forEach(function () {
      this.count = this.count + 1;
    });
    return this.count;
  },

  workingReport: function () {
    // The arrow keeps the `this` of workingReport.
    [1, 2].forEach(() => {
      this.count = this.count + 1;
    });
    return this.count + " " + this.label;
  },
};

try {
  counter.brokenReport();
} catch (error) {
  console.log(error.constructor.name + ":", error.message);
}

console.log(counter.workingReport());

The flip side

Because an arrow has no this of its own, it is the wrong choice for a method that needs to refer to its own object:

const user = {
  name: "Ada",

  // `this` here is the surrounding module scope, not `user`.
  greetArrow: () => "Hi, I am " + this.name,

  greetRegular: function () {
    return "Hi, I am " + this.name;
  },
};

try {
  console.log(user.greetArrow());
} catch (error) {
  console.log("arrow method failed:", error.message);
}

console.log(user.greetRegular());

The arrow version does not just return the wrong name — it fails outright. Inside a module, the surrounding this is undefined, so reading .name from it throws. Calling it as user.greetArrow() makes no difference: an arrow function ignores how it was called.

The rule of thumb: arrow functions for callbacks, regular functions for methods.

Why this matters in React

In modern React you write function components, so you will rarely touch this at all — which is precisely the point. Arrow functions let you pass behaviour around without worrying about what this will be when it eventually runs:

<button onClick={() => setCount(count + 1)}>Increment</button>

That handler runs much later, called by React, from somewhere else entirely. An arrow function does not care.

Common mistakes

  • Braces when you meant an object. () => {} returns nothing; use () => ({}).
  • An arrow as an object method. It will not see the object as this.
  • Assuming arrows are only shorter. The this behaviour is the actual difference; the brevity is a bonus.

Exercise

makeAdder should return a function that adds its argument to base. Rewrite the inner function as an arrow function with an implicit return, then check the output is still 15.

function makeAdder(base) {
  return function (n) {
    return base + n;
  };
}

const addTen = makeAdder(10);
console.log(addTen(5));