JavaScriptIntermediate

JavaScript this Keyword: Global, Object, Function and Arrow

Understand what this means in JavaScript: global scope, object methods, regular functions and arrow functions, and how to fix the lost this problem.

All JavaScript lessons

What you will learn

this is one of the most confusing words in JavaScript, and also one of the most important. The good news: the idea behind it is simple. In this lesson you will learn what this means in the global scope, inside objects, inside regular functions, and inside arrow functions. You will also learn why this sometimes goes missing, and the easy ways to fix it.

The idea in one line

this means “the thing that is calling me right now”.

Imagine the word “I” in English. When Riya says “I am hungry”, I means Riya. When Karan says the same sentence, I means Karan. The sentence is the same, but the meaning of “I” depends on who is speaking.

this works the same way. The same function can have a different this depending on how it is called. (For arrow functions the rule is different, and we will see that soon.)

this in the global context

When you use this outside of any function, it points to the global object. In a browser, the global object is window.

console.log(this === window);   // true (in a browser script)

A modern, safe name that works everywhere (browser, Node.js, workers) is globalThis:

console.log(globalThis === window);   // true in a browser

Variables declared with var at the top level become properties of window. Variables declared with let and const do not:

var oldStyle = "I am on window";
let modern = "I am not on window";

console.log(window.oldStyle);   // "I am on window"
console.log(window.modern);     // undefined

Good to know: in ES modules (<script type="module">) and in Node.js files, the top-level this is not window. It is undefined in modules. For learning in the browser editor, the global this is window.

this inside an object method

When a function is a method of an object and you call it with the dot, this is the object before the dot.

const user = {
  userName: "Riya",
  city: "Delhi",

  greet() {
    console.log("Hi, I am " + this.userName + " from " + this.city);
  }
};

user.greet();   // "Hi, I am Riya from Delhi"

user.greet() has user before the dot, so this is user.

The same method on a different object gives a different result:

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

const riya = { userName: "Riya", introduce };
const karan = { userName: "Karan", introduce };

riya.introduce();    // "I am Riya"
karan.introduce();   // "I am Karan"

It is the same function, but this changes with the object that calls it. That is the whole point of this: it lets one function work with many objects.

A tip: look to the left of the dot

To know what this is in a method call, look at what is on the left of the dot when the function is called.

const school = {
  name: "Green Valley",
  principal: {
    name: "Mrs. Sharma",
    show() {
      console.log(this.name);
    }
  }
};

school.principal.show();   // "Mrs. Sharma"

The thing just before .show() is school.principal, so this is the principal object, not school.

this inside a regular function

A normal function call, with nothing on the left of the dot, has no owner. What this becomes depends on the mode:

Mode this in a plain function call
Normal (sloppy) mode The global object (window)
Strict mode ("use strict", modules, classes) undefined
function showThis() {
  console.log(this);
}

showThis();   // window (in normal mode), undefined (in strict mode)
function showThisStrict() {
  "use strict";
  console.log(this);
}

showThisStrict();   // undefined

This is why a missing this can quietly give you wrong results in one mode, and a loud error in the other.

Gotcha: in a browser, window already has a property called name (it is an empty string). So a lost this.name often shows "" instead of undefined. That can be confusing. Use other names, like userName, while you are learning.

The most common problem: losing this

Here is the number one this bug. A method works perfectly when called with the dot:

const user = {
  userName: "Riya",
  greet() {
    console.log("Hi, I am " + this.userName);
  }
};

user.greet();   // "Hi, I am Riya"

But if you take the method out and call it alone, there is no object on the left of the dot anymore:

const lostGreet = user.greet;

lostGreet();   // "Hi, I am undefined"   (this is window, which has no userName)

In strict mode, you would get a TypeError instead, because this is undefined.

It happens with callbacks too

When you hand a method to another function (a callback), that function calls it without your object:

const user = {
  userName: "Riya",
  greet() {
    console.log("Hi, I am " + this.userName);
  }
};

setTimeout(user.greet, 1000);   // "Hi, I am undefined"  (this is lost)

Fix 1: wrap it in an arrow function

setTimeout(() => user.greet(), 1000);   // "Hi, I am Riya"

Now user.greet() is called with the dot, so this is user again.

Fix 2: bind the method

Every function has a bind method. It creates a new function with this fixed forever:

const boundGreet = user.greet.bind(user);

boundGreet();                    // "Hi, I am Riya"
setTimeout(boundGreet, 1000);    // "Hi, I am Riya"

(bind, call and apply get a full lesson later in the advanced section. For now, just know that bind is the tool that fixes this for good.)

this inside a function inside a method

Another classic trap: a regular function inside a method gets its own this, so it cannot see the object.

const team = {
  teamName: "Dev Team",
  members: ["Riya", "Karan"],

  showMembers() {
    this.members.forEach(function (member) {
      console.log(member + " is in " + this.teamName);
    });
  }
};

team.showMembers();
// "Riya is in undefined"
// "Karan is in undefined"

The inner function (member) { ... } is called by forEach, not by team. So its this is not team.

this inside an arrow function

Now the twist. An arrow function has no this of its own. It simply uses the this of the code around where it was written. This is called lexical this.

Fix the example above by using an arrow function for the callback:

const team = {
  teamName: "Dev Team",
  members: ["Riya", "Karan"],

  showMembers() {
    this.members.forEach((member) => {
      console.log(member + " is in " + this.teamName);
    });
  }
};

team.showMembers();
// "Riya is in Dev Team"
// "Karan is in Dev Team"

The arrow function sits inside showMembers, where this is team. So it uses team too.

But arrow functions make bad object methods

Because an arrow function takes this from the outside, an arrow function written directly in an object does not get that object:

const user = {
  userName: "Riya",
  greet: () => {
    console.log("Hi, I am " + this.userName);
  }
};

user.greet();   // "Hi, I am undefined"

The this here is the this of the surrounding code (the global scope, so window), not user.

The golden rule:

  • Use a normal method (greet() { }) for the object’s own methods.
  • Use an arrow function for callbacks inside those methods.

this in event listeners

In the browser, this also shows up in event handlers. Take this HTML:

<button id="regularBtn">Regular function</button>
<button id="arrowBtn">Arrow function</button>
const regularBtn = document.getElementById("regularBtn");
const arrowBtn = document.getElementById("arrowBtn");

regularBtn.addEventListener("click", function () {
  console.log(this === regularBtn);   // true: this is the button
  this.textContent = "Clicked!";
});

arrowBtn.addEventListener("click", () => {
  console.log(this === arrowBtn);     // false: this is the outside this (window)
});
  • In a regular function, this is the element the listener is attached to.
  • In an arrow function, this is not the element. Use event.currentTarget instead, which works in both. (The event object has its own lesson later.)

Quick summary table

How the function is used What this is
Top-level code (browser script) window (globalThis)
Method call: object.method() The object before the dot
Plain call: myFunction() window in normal mode, undefined in strict mode
Arrow function Not its own. It uses the this of the surrounding code
Event listener (regular function) The element that got the event
Called with new (constructor) The brand-new object being created (a later lesson)
call, apply, bind Whatever you pass in (a later lesson)

You do not need to memorise this table. Remember the main idea: for regular functions, look at how it is called. For arrow functions, look at where it is written.

Real-life use cases

1. A counter object

const counter = {
  count: 0,

  increase() {
    this.count++;
  },

  decrease() {
    this.count--;
  },

  show() {
    console.log("Count is " + this.count);
  }
};

counter.increase();
counter.increase();
counter.decrease();
counter.show();   // "Count is 1"

2. A shopping cart that can chain methods

If a method ends with return this, you can call the next method on the result. This is called method chaining.

const cart = {
  items: [],

  add(name, price) {
    this.items.push({ name, price });
    return this;   // give the cart back
  },

  total() {
    return this.items.reduce((sum, item) => sum + item.price, 0);
  }
};

const total = cart.add("Pen", 10).add("Notebook", 60).add("Bag", 450).total();

console.log(total);   // 520

3. A timer that uses this inside setTimeout

const quiz = {
  question: "What is 2 + 2?",

  startTimer() {
    setTimeout(() => {
      console.log("Time is up for: " + this.question);
    }, 1000);
  }
};

quiz.startTimer();   // after 1 second: "Time is up for: What is 2 + 2?"

The arrow function inside setTimeout keeps the this of startTimer, so it can read this.question. With a regular function, you would lose this.

4. A reusable function shared by many objects

function describe() {
  return `${this.title} costs ₹${this.price}`;
}

const book = { title: "JS Basics", price: 350, describe };
const course = { title: "Web Dev", price: 2999, describe };

console.log(book.describe());     // "JS Basics costs ₹350"
console.log(course.describe());   // "Web Dev costs ₹2999"

5. A button that changes itself

const likeButton = document.getElementById("regularBtn");
let likes = 0;

likeButton.addEventListener("click", function () {
  likes++;
  this.textContent = "Liked " + likes + " time(s)";   // this is the button
});

6. Fixing a method passed as a callback

const logger = {
  prefix: "[LOG]",

  print(message) {
    console.log(this.prefix + " " + message);
  }
};

["start", "end"].forEach(logger.print);
// "undefined start" and "undefined end"  (this is lost)

["start", "end"].forEach((msg) => logger.print(msg));
// "[LOG] start" and "[LOG] end"

["start", "end"].forEach(logger.print, logger);
// "[LOG] start" and "[LOG] end"  (forEach accepts a second argument for this)

Common mistakes

  • Taking a method out of its object (const fn = obj.method; fn();). this is lost. Call it with the dot, wrap it in an arrow function, or use bind.
  • Passing obj.method as a callback to setTimeout, forEach or an event listener. Wrap it: () => obj.method().
  • Using an arrow function as an object method and then reading this.something. Use the normal method syntax.
  • Using a regular function as a callback inside a method and expecting this to be the object. Use an arrow function.
  • Using an arrow function in an event listener and expecting this to be the element. Use event.currentTarget or a regular function.
  • Thinking this is decided where the function is written (for regular functions). It is decided by how it is called. Only arrow functions use the “where written” rule.
  • Using this.name while learning, and getting an empty string. window.name exists. Use a different property name.
  • Forgetting that classes and modules use strict mode, so a lost this is undefined, and you get a TypeError.
  • Using this at the top level of a module and expecting window.

Practice

  1. Create a person object with userName and a method sayHello() that prints "Hello, I am <userName>" using this.
  2. Take the method out: const fn = person.sayHello; fn();. What do you see? Why?
  3. Fix problem 2 in two ways: with an arrow-function wrapper, and with bind.
  4. Create two objects that share the same function showPrice() which uses this.title and this.price.
  5. Write a counter object with count, increase(), reset() and show().
  6. Make increase() return this so you can write counter.increase().increase().show().
  7. Create a team object with a members array and a method that prints each member with the team name. First use a regular function inside forEach, see the bug, then fix it with an arrow function.
  8. Change greet in problem 1 into an arrow function. Does it still work? Explain why in your own words.
  9. In the Practice Editor, click both buttons. Why do the two console messages show different values of this?
  10. Challenge: write a timer object with seconds: 0 and a method start() that uses setInterval with an arrow function to increase this.seconds every second, and stops (use clearInterval) when it reaches 5.

Recap

  • this means “the thing that is calling me”. For regular functions, it is decided by how the function is called.
  • In the global scope of a browser script, this is window (globalThis).
  • In an object method called with a dot (user.greet()), this is the object before the dot.
  • In a plain function call, this is window in normal mode and undefined in strict mode.
  • A method that is taken out of its object, or passed as a callback, loses this. Fix it with an arrow-function wrapper or bind.
  • Arrow functions have no this of their own. They use the this of the code around them. This is perfect for callbacks inside methods, and wrong for the methods themselves.
  • In event listeners, a regular function’s this is the element. An arrow function’s is not.
  • Returning this from a method lets you chain calls.
  • Next you will learn destructuring, spread and rest: modern shortcuts to unpack, copy and combine arrays and objects.