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.
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
thismeans “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-levelthisis notwindow. It isundefinedin modules. For learning in the browser editor, the globalthisiswindow.
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,
windowalready has a property calledname(it is an empty string). So a lostthis.nameoften shows""instead ofundefined. That can be confusing. Use other names, likeuserName, 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,
thisis the element the listener is attached to. - In an arrow function,
thisis not the element. Useevent.currentTargetinstead, 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();).thisis lost. Call it with the dot, wrap it in an arrow function, or usebind. - Passing
obj.methodas a callback tosetTimeout,forEachor 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
thisto be the object. Use an arrow function. - Using an arrow function in an event listener and expecting
thisto be the element. Useevent.currentTargetor a regular function. - Thinking
thisis 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.namewhile learning, and getting an empty string.window.nameexists. Use a different property name. - Forgetting that classes and modules use strict mode, so a lost
thisisundefined, and you get aTypeError. - Using
thisat the top level of a module and expectingwindow.
Practice
- Create a
personobject withuserNameand a methodsayHello()that prints"Hello, I am <userName>"usingthis. - Take the method out:
const fn = person.sayHello; fn();. What do you see? Why? - Fix problem 2 in two ways: with an arrow-function wrapper, and with
bind. - Create two objects that share the same function
showPrice()which usesthis.titleandthis.price. - Write a
counterobject withcount,increase(),reset()andshow(). - Make
increase()returnthisso you can writecounter.increase().increase().show(). - Create a
teamobject with amembersarray and a method that prints each member with the team name. First use a regular function insideforEach, see the bug, then fix it with an arrow function. - Change
greetin problem 1 into an arrow function. Does it still work? Explain why in your own words. - In the Practice Editor, click both buttons. Why do the two console messages show different values of
this? - Challenge: write a
timerobject withseconds: 0and a methodstart()that usessetIntervalwith an arrow function to increasethis.secondsevery second, and stops (useclearInterval) when it reaches 5.
Recap
thismeans “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,
thisiswindow(globalThis). - In an object method called with a dot (
user.greet()),thisis the object before the dot. - In a plain function call,
thisiswindowin normal mode andundefinedin 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 orbind. - Arrow functions have no
thisof their own. They use thethisof the code around them. This is perfect for callbacks inside methods, and wrong for the methods themselves. - In event listeners, a regular function’s
thisis the element. An arrow function’s is not. - Returning
thisfrom a method lets you chain calls. - Next you will learn destructuring, spread and rest: modern shortcuts to unpack, copy and combine arrays and objects.