cf.completefrontendCode editorOpen lab
THE JAVASCRIPT FIELD GUIDE

Arrow functions & lexical this

Learn why arrow functions inherit this, how that helps callbacks and timers, and why arrows are the wrong shape for object methods and many event handlers.

By the end, you can
  • 01
    Explain lexical thisDescribe why an arrow uses the this from where it was written.
  • 02
    Choose callback shapesUse arrows in timers and array callbacks when they need the surrounding method’s this.
  • 03
    Avoid method trapsPrefer method shorthand for object methods and event.currentTarget for event code.

The sticky-note rule

You already met arrow function syntax in the Arrow functions lesson. This lesson goes deeper on the one arrow feature that changes how real programs feel: arrows do not create their own this. A regular function asks, “who is calling me right now?” every time it runs. An arrow never asks. It uses the this from the place where it was written.

That behavior is called lexical this. The word lexical means “based on where the code is written,” the same idea you saw in Scope and lexical environments and Closures. Variables are found by looking outward through the written scopes. An arrow’s this is found the same way.

One sentence definition

An arrow function captures this from its surrounding code; call, apply, and bind cannot change that captured value.

Real-life analogyAn arrow carries a sticky note

Imagine writing a sticky note inside a room: “use this room.” If you hand the note to someone in the hallway, the note does not change itself to say “hallway.” Arrow functions behave like that note.

In real life: A room label on the door
In JavaScript: The surrounding function’s this
In real life: A sticky note saying “same room as where I was written”
In JavaScript: An arrow function’s lexical this
In real life: Moving the note to another desk
In JavaScript: Passing the arrow to a callback, timer, or call
In real life: The note still names the original room
In JavaScript: The arrow still uses the captured this

Where the analogy stops: A sticky note is copied text; an arrow captures a live binding to a this value chosen by JavaScript. The analogy explains why moving the arrow later does not relabel it.

That makes arrows wonderful in callbacks and timers, where a regular function is often called later with no useful receiver. It also makes arrows a poor fit for object methods and for event handlers that rely on the browser’s this. By the end, you will know which shape to choose before you type the function.

Lexical this: arrows do not ask the call site

STEP THROUGH

The this keyword lesson focuses on call-site rules: default, implicit, explicit, and new binding. Regular functions participate in those rules. If you run obj.fn(), the receiver before the dot supplies this. If you run fn.call(other), call supplies it explicitly.

Arrows are different. Their this is chosen when the arrow is created, by the surrounding code. The next experiment creates an arrow inside an object method. Then it tries to use call to relabel the arrow. Predict the output before you step.

Can call relabel an arrow?
Step 0 of 4Ready
Your turn: follow the blue line

Predict whether line 8 prints studio or garage, then step through the call.

Running in
  1. script
Next: line 2
Click the blue line to take the next stepPop out in the code editor (opens in a new tab)JavaScript
"use strict";  name: "studio",  makeReader() {    return () => this.name;  }}; const reader = room.makeReader();console.log(reader.call({ name: "garage" }));
CallStoreChangeResultRun = next line. Ran = already executed.
Recent returnsNothing yet. Start with the blue line.
A guided replay recorded from real JavaScript calls, not an engine debugger. Step follows executed statements; Back reviews a snapshot. Reset starts a fresh run.

The surprising line is the last one. reader.call looks as if it should set this to { name: "garage" }. It would work on a regular function. But reader is an arrow that was created while room.makeReader() was running, and that method’s this was room. The arrow keeps reading the room.

Real-life analogyA regular function checks the name badge at the door

Regular functions are like visitors who check the sign at the door every time they enter. Arrows are like visitors with a badge already printed: moving them to a new line does not reprint the badge.

In real life: A visitor asks security, “which company am I visiting?”
In JavaScript: A regular function asks the call site for this
In real life: A badge printed before the visit
In JavaScript: An arrow’s this decided at creation time
In real life: Putting the visitor in a different line
In JavaScript: Using call, apply, or bind later

Where the analogy stops: Real people can ask again. Arrow functions cannot; there is no own this binding to replace.

Fun fact: bind still returns a function

Calling bind on an arrow is allowed, but it cannot bind this. It can still pre-fill arguments, so the method is not useless; it just cannot relabel the arrow’s this.

Arrows in callbacks & timers

INTERACTIVE

Callback code often runs later, or inside another helper. A timer API does not call your callback as timer.callback(). Array methods do not call your callback as object.callback(). They usually call it as a plain function. In strict code, a plain regular-function call has this set to undefined.

The next demo uses a synchronous helper named runLater so you can see the rule without waiting for a real clock. It mimics the important timer behavior: a callback is stored, then invoked as callback(). Switch the callback style and follow the frames.

Switch the timer callback
Step 0 of 4Ready
Your turn: follow the blue line

Switch the callback style. Regular functions ask who called them; arrows keep the method’s this.

Running in
  1. script
Next: line 6
Click the blue line to take the next stepPop out in the code editor (opens in a new tab)JavaScript
"use strict";function runLater(callback) {  callback();}   label: "Focus",  seconds: 0,  start() {    runLater(function () {      this.seconds += 1;      console.log(this.label + ": " + this.seconds);    });  }}; try {  timer.start();} catch (error) {  console.log(error.name);}
CallStoreChangeResultRun = next line. Ran = already executed.
Recent returnsNothing yet. Start with the blue line.
Change the callback passed to runLater

Changing the callback starts a fresh run with real JavaScript behavior.

A guided replay recorded from real JavaScript calls, not an engine debugger. Step follows executed statements; Back reviews a snapshot. Reset starts a fresh run.

With the regular callback, the method call timer.start() correctly gives start a timer-shaped this. But the callback is a separate function. When runLater calls it with no receiver, the callback asks “who called me?” and the answer is “nobody useful.” The update fails.

With the arrow callback, the callback never asks runLater for a new this. It uses the this that start already had. This is the common reason you see arrows inside setTimeout, setInterval, map, forEach, and promise callbacks.

Three callback patterns with the same outputPop out in the code editor (opens in a new tab)JavaScript
"use strict";const tagger = {  prefix: "JS-",  ids: [1, 2, 3],  withArrow() {    return this.ids.map((id) => this.prefix + id);  },  withSelf() {    const self = this;    return this.ids.map(function (id) {      return self.prefix + id;    });  },  withBind() {    return this.ids.map(function (id) {      return this.prefix + id;    }.bind(this));  }}; console.log(tagger.withArrow().join("|"));console.log(tagger.withSelf().join("|"));console.log(tagger.withBind().join("|"));

Older code often wrote const self = this before a regular callback, or used .bind(this) after it. Those patterns are still useful to recognize. In modern code, the arrow version is usually shorter and clearer: “use the same this as the method I’m inside.”

Arrows as methods: the trap

COMPARE

The arrow rule helps when an inner callback needs the outer method’s this. It hurts when the arrow itself is supposed to be the method. Object methods normally need the object on the left of the dot to become this. An arrow method ignores that dot.

Arrow method vs method shorthandPop out in the code editor (opens in a new tab)JavaScript
"use strict";function makeCounter() {  return {    count: 0,    inc: () => this.count++  };} const counter = makeCounter.call(undefined);try {  counter.inc();} catch (error) {  console.log(error.name);} const safeCounter = {  count: 0,  inc() {    this.count++;    return this.count;  }};console.log(safeCounter.inc());

In the strict factory shown above, the outer this captured by the arrow is undefined. So inc: () => this.count++ does not mean “increment counter.count.” It means “increment count on the outer this,” and there is no outer object to update.

Production-safe this demos

Notice that the working object uses inline method shorthand: inc() { ... }. For this examples, avoid building a local object from a separately declared function, because production minifiers can rewrite the call in a way that hides the receiver. Inline methods keep the demonstration honest.

Regular function vs arrow for this
SituationRegular functionArrow function
Method call obj.fn()this is usually objIgnores obj; uses outer this
Plain call fn() in strict codethis is undefinedStill uses outer this
Callback inside a methodOften loses the method’s this unless boundKeeps the method’s this
Event listenerBrowser sets this to event.currentTargetUses outer this; prefer event.currentTarget
call / apply / bindCan set or pre-bind thisCannot change this; can still pass arguments
newCan construct if written as a constructorThrows: arrows are not constructors

Class fields are the one “method-looking” exception worth knowing: handleClick = () => { ... } creates an arrow per instance, so it captures that instance’s this. That is a bound-method pattern you will see again in Losing this and in the Classes stage. For ordinary object literals, choose method shorthand.

this in event handlers

REAL BUTTON

The DOM and events come later in the course, but this in event handlers is too common to ignore. Browsers call a regular addEventListener listener with this set to the element whose listener is running. The event object gives you the same element as event.currentTarget.

Arrow listeners do not receive that browser-provided this. They keep the outer this instead. In React components and ES modules, that outer value is not the button. Click this real button and compare both listener styles.

Click a real button and compare event this
Two event listenersPop out in the code editor (opens in a new tab)JavaScript
button.addEventListener("click", function (event) {  console.log(this === event.currentTarget);}); button.addEventListener("click", (event) => {  console.log(this === event.currentTarget);  console.log(event.currentTarget.textContent);});
Live button0 listener calls
  • WaitingClick the button to log both listeners.
Try it yourself

Click the real button or dispatch a click. Two listeners run: a regular function and an arrow. Compare their this values with event.currentTarget.

This uses addEventListener through a React ref after mount, so the result comes from the browser’s event system.

The practical rule is simple: use event.currentTarget instead of this when in doubt. It works with a regular listener, an arrow listener, and code that gets refactored later.

Refactor event code to currentTargetPop out in the code editor (opens in a new tab)JavaScript
button.addEventListener("click", (event) => {  event.currentTarget.disabled = true;  event.currentTarget.textContent = "Saved";});

Where you will use this

SORT

The choice is not “arrows are modern, regular functions are old.” The choice is “does this function need its own caller-chosen this, or should it keep the surrounding one?” That question appears in UI handlers, data transformations, timers, class components, and tests.

Regular function or arrow here?
  • An object method that reads this.count
  • A forEach callback inside a method that reads this.prefix
  • An event listener that intentionally uses this as the clicked element
  • A map callback that only uses its parameter
  • A class field handler that should stay bound when passed around
  • A function that will be called with new

    It belongs in Regular function. Arrow functions cannot be constructors. They do not have [[Construct]], so new throws.

Try it yourself
0 of 6 correct

Sort each situation by the function shape that makes this safest.

Choose a category for every card. You can change an answer at any time; Reset clears them all.

A useful habit is to write the receiver in your sentence. If the sentence is “this object should do the work,” use method shorthand or a regular function. If the sentence is “this inner callback should use the same object as the surrounding method,” use an arrow. If no this is involved, choose the style that is easiest to read.

Common misconceptions

  • “Arrows bind this to themselves.” They do the opposite: they have no own this binding.
  • “bind fixes every function.” bind fixes regular functions. It cannot change an arrow’s captured this.
  • “Arrows are always better callbacks.” They are great when you want lexical this, but a regular event listener is useful when you intentionally want the browser’s this.
  • “An arrow method reads the object before the dot.” The dot does not matter to an arrow. Use method shorthand for object behavior.
  • “event.target and event.currentTarget are the same.” They match for simple buttons, but child elements can be the target. The listener’s element is currentTarget.

Practice exercises

5 EXERCISES
Exercise 1 · Warm-upPredict an arrow method

Read the code. What exact error name is printed?

Starter codePop out in the code editor (opens in a new tab)JavaScript
"use strict";
function makeCounter() {
  return {
    count: 0,
    inc: () => this.count++
  };
}
const counter = makeCounter.call(undefined);
try {
  counter.inc();
} catch (error) {
  console.log(error.name);
}

Answer, then press Check. Spacing and letter case don’t matter.

    Exercise 2 · PracticeFix a timer callback

    Rewrite a broken regular timer callback as an arrow. What does the fixed code print?

    Starter codePop out in the code editor (opens in a new tab)JavaScript
    "use strict";
    const timer = {
      label: "Tea",
      start() {
        setTimeout(() => {
          console.log(this.label);
        }, 0);
      }
    };
    timer.start();

    Answer, then press Check. Spacing and letter case don’t matter.

      Exercise 3 · PracticeConvert const self = this

      Replace the inner function and self alias with an arrow callback.

      Starter codePop out in the code editor (opens in a new tab)JavaScript
      "use strict";
      const list = {
        prefix: "#",
        values: [1, 2, 3],
        labels() {
          const self = this;
          return this.values.map(function (value) {
            return self.prefix + value;
          });
        }
      };
      console.log(list.labels().join(","));
      
      // Convert labels() to the arrow version.

      Answer, then press Check. Spacing and letter case don’t matter.

        Exercise 4 · PracticeChoose the method shape

        A cart object has an add method that changes this.total. Which style is safer?

        Answer, then press Check. Spacing and letter case don’t matter.

          Exercise 5 · ChallengeFix an event handler

          Make this arrow listener disable the clicked button without relying on this.

          Starter codePop out in the code editor (opens in a new tab)JavaScript
          button.addEventListener("click", () => {
            this.disabled = true;
          });

          Answer, then press Check. Spacing and letter case don’t matter.

            Check your understanding

            7 QUESTIONS
            Lesson quiz · 7 questionsScore: first tries count
            1. Question 1 of 7What does lexical this mean for an arrow function?

              Choose an answer to see the explanation.

            2. Question 2 of 7What does the call-relabeled arrow print?

              Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
              "use strict";
              const tool = {
                name: "Brush",
                show() {
                  const read = () => this.name;
                  console.log(read.call({ name: "Drill" }));
                }
              };
              tool.show();

              Choose an answer to see the explanation.

            3. Question 3 of 7Which callback is usually best inside a method when the callback needs the method’s this?

              Choose an answer to see the explanation.

            4. Question 4 of 7What error name does the arrow method print?

              Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
              "use strict";
              function makeCounter() {
                return {
                  count: 0,
                  inc: () => this.count++
                };
              }
              const counter = makeCounter.call(undefined);
              try {
                counter.inc();
              } catch (error) {
                console.log(error.name);
              }

              Choose an answer to see the explanation.

            5. Question 5 of 7In a browser addEventListener regular function listener, what is this normally set to?

              Choose an answer to see the explanation.

            6. Question 6 of 7What happens if you use new with an arrow function?

              Choose an answer to see the explanation.

            7. Question 7 of 7When in doubt inside an event handler, which value should you read instead of relying on this?

              Choose an answer to see the explanation.

            Key takeaways

            • Regular functions get this from how they are called.
            • Arrow functions get this from where they are written.
            • Use arrows for callbacks that need the surrounding method’s this.
            • Use method shorthand for object methods that read or write this.
            • In event handlers, event.currentTarget is clearer than relying on this.

            Arrow functions are functions with lexical this: they keep the surrounding this instead of receiving a new one from the call site.

            Up next: Losing this.

            CompleteFrontend Clear concepts. Working examples.