Learn
🤖 AI Hub 📰 News 📖 Dictionary 🧰 Resources
Grow
💼 Jobs 🚀 Community 🏆 Challenges
Explore by Stage
🌑 Zero 📚 Learn 🔧 Practice 🏗 Build 🚀 Launch 💰 Earn
Account
Login Sign Up
🏗️HTML 🎨CSS ⚡JavaScript 🐙Git & GitHub ⚛️React Soon 📘TypeScript Soon 🐍Python 🗄️Database & SQL Soon 🌐Web Development Soon ☁️Cloud & DevOps Soon 🤖AI & ML Soon 📄Resume Prep Soon 🎤Interview Prep Soon 💼Freelancing Soon 🚀SaaS & Side Projects Soon 🤖Automation Soon
Advanced 🕑 11 min read 👁 2 views 📅 Oct 21, 2026

The this Keyword Explained Simply

Understand the JavaScript this keyword with one simple idea: like the word I, it means whoever is calling. Fix lost this in callbacks with bind and arrow functions, and use this in events and classes.

Who Is “I”?

Read this sentence: “I am the class topper.” Who is “I”? You cannot tell from the sentence alone. If Meera says it, “I” means Meera. If Kabir reads the same sentence aloud, “I” means Kabir. The words stay the same; the meaning depends on who is speaking.

The JavaScript keyword this works exactly the same way. A function that uses this is like a sentence that uses “I”. What this points to is not decided when you write the function. It is decided each time the function is called, by how it is called.

That one rule explains almost every this puzzle. In this lesson you will see this in object methods, plain functions, callbacks, arrow functions, event handlers and classes, and learn how to control it with bind, call and apply. You should already know objects and the class syntax from the JavaScript Classes and Objects lesson.

this in Object Methods

When you call a function as a method, with an object before the dot, this is that object. The object before the dot is the speaker.

const aarav = {
  name: "Aarav",
  city: "Pune",
  introduce() {
    return "I am " + this.name + " from " + this.city;
  },
};

const diya = {
  name: "Diya",
  city: "Guwahati",
  introduce: aarav.introduce,   // the very same function
};

console.log(aarav.introduce());   // I am Aarav from Pune
console.log(diya.introduce());    // I am Diya from Guwahati

// The function did not change. Only the speaker (the object before the dot) changed.

Both objects use the same function, yet the result is different, because the object before the dot is different. This is what lets one method work for thousands of objects.

this in a Normal Function Call

What if there is nothing before the dot? Then there is no speaker. In strict mode, this is undefined. Strict mode is a safer version of JavaScript that you switch on with "use strict", and it is always on inside classes and modules.

function whoIsSpeaking() {
  "use strict";
  return this;
}

console.log(whoIsSpeaking());   // undefined (no object before the dot)

const mic = { owner: "Anchor", whoIsSpeaking };
console.log(mic.whoIsSpeaking() === mic);   // true

In old, non-strict scripts, this in a plain call falls back to the global object (window in a browser). That hides bugs, because your code silently reads and writes global variables. The strict behaviour is better: a mistake fails loudly.

Losing this in a Callback

This is the most common this bug. When you pass a method to another function, you pass only the function, not the object it came from. Later, the other function calls it with nothing before the dot, so this is lost. It is like recording Riya saying “I am the speaker” and playing the recording to a room that cannot see her.

class Speaker {
  constructor(name) {
    this.name = name;
  }

  introduce() {
    console.log("I am " + this.name);
  }
}

function passTheMic(callback) {
  callback();   // called with nothing before the dot
}

const riya = new Speaker("Riya");
riya.introduce();          // I am Riya

passTheMic(riya.introduce);   // ERROR: TypeError: Cannot read properties of undefined (reading 'name')

The error message says JavaScript tried to read name from undefined. That is your sign that this was lost. The same thing happens with setTimeout(riya.introduce, 1000) and addEventListener("click", riya.introduce). You will fix it in two ways below.

Arrow Functions Take this from Where They Are Written

Arrow functions follow a different rule. They do not have their own this. Inside an arrow function, this means whatever it means in the code around it, at the place where the arrow function is written. That makes arrow functions perfect for callbacks inside methods.

class Teacher {
  constructor(name, students) {
    this.name = name;
    this.students = students;
  }

  callRoll() {
    // An arrow function has no this of its own: it uses callRoll's this
    this.students.forEach((student) => {
      console.log(this.name + ": " + student + ", are you present?");
    });
  }

  callRollWithNormalFunction() {
    this.students.forEach(function (student) {
      // A normal function gets its own this, and forEach gives it none
      console.log(student + " is asked by", this);
    });
  }
}

const teacher = new Teacher("Mrs. Banerjee", ["Kabir", "Sana"]);

teacher.callRoll();
// Mrs. Banerjee: Kabir, are you present?
// Mrs. Banerjee: Sana, are you present?

teacher.callRollWithNormalFunction();
// Kabir is asked by undefined
// Sana is asked by undefined
Kind of functionWhat decides this
Normal function or methodHow it is called (the object before the dot)
Arrow functionWhere it is written (the surrounding code)

The flip side: do not use an arrow function as an object method. In { name: "Aarav", introduce: () => this.name }, the arrow function takes this from outside the object, so it never points to the object.

bind, call and apply

Sometimes you want to choose this yourself, like handing someone a script and saying “read this as Meera”. Every function has three methods for that:

  • fn.call(obj, a, b) runs fn now, with this set to obj.
  • fn.apply(obj, [a, b]) does the same, but takes the arguments as an array.
  • fn.bind(obj) does not run anything. It returns a new function whose this is fixed to obj forever, which is exactly what you need for callbacks.
function introduce(greeting, ending) {
  return greeting + ", I am " + this.name + ending;
}

const meera = { name: "Meera" };
const kabir = { name: "Kabir" };

// call: run now, with this = meera, arguments one by one
console.log(introduce.call(meera, "Namaste", "!"));        // Namaste, I am Meera!

// apply: run now, with this = kabir, arguments as an array
console.log(introduce.apply(kabir, ["Hello", "."]));       // Hello, I am Kabir.

// bind: do not run now; return a new function with this fixed forever
const meeraSays = introduce.bind(meera, "Vanakkam");
console.log(meeraSays("!!"));                              // Vanakkam, I am Meera!!

// bind fixes the lost-this problem from before
class Speaker {
  constructor(name) {
    this.name = name;
  }
  introduce() {
    console.log("I am " + this.name);
  }
}

function passTheMic(callback) {
  callback();
}

const riya = new Speaker("Riya");
passTheMic(riya.introduce.bind(riya));   // I am Riya
passTheMic(() => riya.introduce());      // I am Riya

The last two lines show the two standard fixes for a lost this: bind the method, or wrap the call in an arrow function so the method is called with the object before the dot.

this in Event Handlers

When the browser runs a normal function as an event handler, it sets this to the element the listener is attached to. An arrow function handler keeps the outer this instead. In both cases, event.currentTarget gives you the element reliably, which is why many developers prefer it. You learned about the event object in the JavaScript Events lesson.

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>this in Event Handlers</title>
  <style>
    body { font-family: sans-serif; padding: 16px; }
    button { padding: 10px 14px; margin: 4px; font-size: 16px; }
    .picked { background: #ffd54f; }
  </style>
</head>
<body>
  <p>Pick your fest event:</p>
  <button>Coding</button>
  <button>Quiz</button>
  <button>Dance</button>
  <p id="out"></p>

  <script>
    const out = document.querySelector("#out");

    document.querySelectorAll("button").forEach((button) => {
      // A normal function: the browser sets this to the element
      button.addEventListener("click", function () {
        this.classList.toggle("picked");
        console.log("this is the button:", this.textContent);
      });

      // An arrow function: this is NOT the button, so use event.currentTarget
      button.addEventListener("click", (event) => {
        console.log("this is the button?", this === event.currentTarget);   // false
        out.textContent = "Last clicked: " + event.currentTarget.textContent;
      });
    });
  </script>
</body>
</html>

this in Classes

Inside class methods, this is the instance the method is called on, as you saw with the bank account. Classes run in strict mode, so a lost this gives undefined and a clear error. There are two common ways to make a class method safe to pass around:

  • Bind it once in the constructor: this.speak = this.speak.bind(this) or bind it where you add the listener.
  • Write it as an arrow function in a class field: stop = () => { ... }. Each instance gets its own copy, with this fixed to that instance.

Real-Life Example: A Debate Speaker Board

At an inter-college debate, each speaker gets a card on the board. Pressing “Give the mic” makes that speaker the current one and counts their turns. Each card is a Speaker object. Its click handler must know which speaker it belongs to, so this example uses both fixes: bind for speak and an arrow class field for stop.

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Debate Speaker Board</title>
  <style>
    body { font-family: sans-serif; padding: 16px; max-width: 420px; }
    .speaker { display: flex; justify-content: space-between; align-items: center;
               border: 1px solid #ccc; border-radius: 8px; padding: 8px 12px; margin: 8px 0; }
    .speaking { background: #e3f2fd; border-color: #1e88e5; }
    button { padding: 6px 10px; }
  </style>
</head>
<body>
  <h2>Inter-College Debate</h2>
  <div id="board"></div>
  <p id="now">Nobody is speaking.</p>

  <script>
    const board = document.querySelector("#board");
    const now = document.querySelector("#now");
    let current = null;

    class Speaker {
      constructor(name, college) {
        this.name = name;
        this.college = college;
        this.turns = 0;

        this.box = document.createElement("div");
        this.box.className = "speaker";
        this.label = document.createElement("span");
        this.button = document.createElement("button");
        this.button.textContent = "Give the mic";
        this.box.append(this.label, this.button);
        board.append(this.box);

        // Option 1: bind once in the constructor
        this.button.addEventListener("click", this.speak.bind(this));
        this.update();
      }

      speak() {
        if (current) {
          current.stop();
        }
        current = this;
        this.turns++;
        this.box.classList.add("speaking");
        now.textContent = "\"I am " + this.name + " from " + this.college + ", and I say...\"";
        this.update();
      }

      // Option 2: an arrow function in a class field keeps this automatically
      stop = () => {
        this.box.classList.remove("speaking");
      };

      update() {
        this.label.textContent = this.name + " (" + this.college + "), turns: " + this.turns;
      }
    }

    new Speaker("Ishita", "St. Xavier's, Kolkata");
    new Speaker("Farhan", "Govt. Arts College, Chennai");
    new Speaker("Gurpreet", "DAV College, Chandigarh");
  </script>
</body>
</html>

Try these changes:

  1. Remove .bind(this) from the listener and click a button. The console shows an error, because this is now the button element, not the speaker. Put it back.
  2. Add a fourth speaker from a college in your city.
  3. Add a “Time up” button to each card that calls stop directly as the listener. It works without bind. Why?

Common Mistakes

  • Passing a method as a callback, like setTimeout(timer.start, 1000). The object is lost. Use timer.start.bind(timer) or () => timer.start().
  • Using an arrow function as an object method. It does not get the object as this. Use the method shorthand introduce() { ... }.
  • Using a normal function inside a method, for example in forEach or setTimeout. It gets its own this. Use an arrow function there.
  • Thinking bind changes the original function. It returns a new function. Save it, and use the same saved function if you later call removeEventListener.

Practice Task: A Study Timer That Keeps Its this

Nisha wants a study timer that counts down and then tells her to take a break. Complete the StudyTimer class so that:

  • start() uses setInterval and an arrow function, so this still points to the timer.
  • It logs the seconds left every second, then stops and logs “Break time, Nisha!”.
  • You pass timer.start to startLater without losing this, once with bind and once with an arrow function.
class StudyTimer {
  constructor(student, seconds) {
    this.student = student;
    this.secondsLeft = seconds;
  }

  start() {
    // TODO 1: use setInterval to run once every second.
    //   Each time: reduce this.secondsLeft by 1 and log "Seconds left: <n>".
    //   When it reaches 0: stop the interval and log "Break time, <student>!"
    //   Hint: which kind of function keeps this pointing to the timer?
  }
}

const timer = new StudyTimer("Nisha", 3);

// TODO 2: start the timer by passing timer.start to the helper below,
//   without losing this. Try both bind and an arrow function.
function startLater(callback) {
  setTimeout(callback, 500);
}
Show one possible answer (try it yourself first!)
class StudyTimer {
  constructor(student, seconds) {
    this.student = student;
    this.secondsLeft = seconds;
  }

  start() {
    const id = setInterval(() => {
      this.secondsLeft--;
      console.log("Seconds left: " + this.secondsLeft);
      if (this.secondsLeft === 0) {
        clearInterval(id);
        console.log("Break time, " + this.student + "!");
      }
    }, 1000);
  }
}

const timer = new StudyTimer("Nisha", 3);

function startLater(callback) {
  setTimeout(callback, 500);
}

startLater(timer.start.bind(timer));
// or: startLater(() => timer.start());

// Seconds left: 2
// Seconds left: 1
// Seconds left: 0
// Break time, Nisha!

Key Takeaways

  • this is like the word “I”: its meaning depends on who calls the function, not where it is defined.
  • In obj.method(), this is obj. In a plain call in strict mode, it is undefined.
  • Passing a method as a callback loses this. Fix it with bind or an arrow function wrapper.
  • Arrow functions have no this of their own; they use the this of the code around them.
  • call and apply run a function with a chosen this; bind returns a new function with this fixed.
  • In event handlers, prefer event.currentTarget to find the element.

❓ Frequently Asked Questions

In Java, this always means the current object inside a class. In JavaScript, functions are values that can be passed around and attached to any object, so this is decided at call time. Arrow functions and bind give you the fixed behaviour when you need it.
Use arrow functions for callbacks inside methods. Use normal method syntax for the methods themselves, so that this points to the object they are called on.
In a normal browser script, this at the top level is the window object. In a JavaScript module, it is undefined.
No. Once a function is bound, binding it again does not change its this. Only the first bind counts.
Was this lesson helpful?

🎯 Finished this lesson?

🔗 Related Tutorials
🤖 Got a Doubt? Ask AI
Get instant answers in Hindi or English
🤖
Z2T Bot
Online
Ask me anything about this tutorial — I'm here! 👋