Understanding this in JavaScript: Who Is Calling?

Who is this for? If
thisin JavaScript has ever confused you — sometimes it is the object, sometimes it is the window, sometimes it is undefined — this guide breaks it down simply using one core idea that makes everything click.
Table of Contents
Introduction
this is one of the most misunderstood keywords in JavaScript. Most beginners assume it works like in other languages — a fixed reference to "the current object." In JavaScript, this is different. Its value is not set when you write the code. It is set when the function is called.
The single most important idea in this entire article:
thisrefers to whoever is calling the function at that moment.
Hold that thought. Everything you learn in this guide flows from it.
💡 Open your browser console (
F12→ Console tab) and try every example as you read.
1. What Does this Represent?
this is a special keyword that automatically refers to an object — specifically, the object that is responsible for the current function call.
Think of it like this: when a function runs, JavaScript looks around and asks "Who triggered this call?" Whatever the answer is — that is this.
A human analogy
Imagine you receive a letter that says: "My phone number is 98765." The word "My" only makes sense if you know who wrote the letter. If Priya wrote it, "My" means Priya. If Rahul wrote it, "My" means Rahul.
this works the same way. The word this inside a function only has a specific meaning once you know who called that function.
const priya = {
name: "Priya",
greet: function() {
console.log("Hello, I am " + this.name);
// ↑
// this = whoever called greet()
}
};
priya.greet(); // "Hello, I am Priya"
// ↑
// priya called greet() — so this = priya
priya is the caller. So this inside greet is priya. Therefore this.name is "Priya".
2. this in the Global Context
When code runs outside any function or object — at the top level of your script — this refers to the global object.
In a browser, the global object is
windowIn Node.js, the global object is
global
// In the browser — at the top level
console.log(this); // Window {...}
console.log(this === window); // true
Global variables become properties of this
When you declare a variable with var at the top level (not inside a function or block), it becomes a property of the global object:
var appName = "MyApp";
console.log(window.appName); // "MyApp"
console.log(this.appName); // "MyApp"
⚠️ This is one reason
varis discouraged in modern code — it pollutes the global object. Variables declared withletandconstdo not become properties ofthis.
let siteName = "MyBlog";
console.log(this.siteName); // undefined — let does not attach to global
console.log(window.siteName); // undefined
this in the global context — summary
// Browser — top-level code
console.log(this); // Window
console.log(this === window); // true
// Node.js — top-level code
console.log(this); // {} (the module's exports object)
console.log(this === global); // false at the top level in modules
In practice, you rarely use this at the global level intentionally. The important contexts are inside objects and functions — which we cover next.
3. this Inside Objects
When a function is defined as a method of an object (a property that holds a function), and you call that method using the object, this inside that method refers to the object itself.
Basic object method
const user = {
name: "Priya",
email: "priya@example.com",
greet: function() {
console.log("Hi, I am " + this.name);
console.log("Reach me at " + this.email);
}
};
user.greet();
// Hi, I am Priya
// Reach me at priya@example.com
user.greet() — user is the caller. So this = user. And this.name = user.name = "Priya".
Accessing multiple properties with this
const product = {
name: "Laptop",
price: 75000,
discount: 0.10,
getFinalPrice: function() {
const saving = this.price * this.discount;
const finalPrice = this.price - saving;
return finalPrice;
},
describe: function() {
console.log(this.name + " costs ₹" + this.getFinalPrice());
}
};
console.log(product.getFinalPrice()); // 67500
product.describe(); // Laptop costs ₹67500
Every property and method of product is accessible via this inside any of its methods — because product is the caller.
Multiple objects, same method shape
const student1 = {
name: "Priya",
course: "Computer Science",
introduce: function() {
console.log("I am " + this.name + ", studying " + this.course);
}
};
const student2 = {
name: "Rahul",
course: "Mathematics",
introduce: function() {
console.log("I am " + this.name + ", studying " + this.course);
}
};
student1.introduce(); // I am Priya, studying Computer Science
student2.introduce(); // I am Rahul, studying Mathematics
Even though the introduce function body is identical in both objects, this gives the right result each time — because this is set by who calls the function, not by where the function was written.
Nested objects
When a method is on a nested object, this refers to the immediately containing object — the direct caller:
const company = {
name: "TechCorp",
ceo: {
name: "Ananya",
introduce: function() {
console.log("I am " + this.name + ", CEO.");
// this = company.ceo (the direct caller), NOT company
}
}
};
company.ceo.introduce(); // I am Ananya, CEO.
this is company.ceo — not company — because ceo is the object that directly owns introduce.
4. this Inside Functions
This is where this starts to feel tricky. The behaviour depends on how the function is called — not where it was defined.
Regular function called without an object — this is global (or undefined)
When you call a plain function — not attached to any object — this becomes the global object in non-strict mode, or undefined in strict mode.
function showThis() {
console.log(this);
}
showThis(); // Window {...} (browser, non-strict mode)
There is no object before the function call — no something.showThis() — so JavaScript falls back to the global object.
Strict mode — this becomes undefined
"use strict";
function showThis() {
console.log(this); // undefined — strict mode prevents the global fallback
}
showThis();
Strict mode is a safer way to write JavaScript — the undefined result makes accidental global pollution easier to catch as an error.
The lost this problem — a very common mistake
const timer = {
message: "Time is up!",
start: function() {
setTimeout(function() {
console.log(this.message); // undefined — `this` is lost here!
}, 1000);
}
};
timer.start();
Why did this fail?
timer.start() is called on timer, so this = timer inside start. But setTimeout calls its callback as a plain function — not as a method of timer. By the time the callback runs, there is no object calling it, so this defaults back to window (or undefined in strict mode). window.message does not exist.
This is the most common this-related bug in JavaScript.
The fix — use an arrow function
Arrow functions do not have their own this. They inherit this from the surrounding code where they were written — which is called the lexical scope.
const timer = {
message: "Time is up!",
start: function() {
// Arrow function — this is inherited from start(), where this = timer
setTimeout(() => {
console.log(this.message); // "Time is up!" ✓
}, 1000);
}
};
timer.start(); // "Time is up!" (after 1 second)
The arrow function inside setTimeout does not create a new this — it borrows this from start, where this is timer. Problem solved.
Regular function vs arrow function — this behaviour
const obj = {
value: 42,
// Regular method — has its own `this` (= obj when called as obj.method)
regularMethod: function() {
console.log("Regular:", this.value); // 42
},
// Arrow method — inherits `this` from where the object is defined
arrowMethod: () => {
console.log("Arrow:", this.value); // undefined
// `this` here is the global object, not obj
// because arrows inherit from the enclosing lexical scope
}
};
obj.regularMethod(); // Regular: 42
obj.arrowMethod(); // Arrow: undefined
🔑 Key rule: Do not use arrow functions as object methods when you need
thisto refer to the object. Use regular functions for methods. Use arrow functions for callbacks inside methods.
5. How Calling Context Changes this
The same function can produce completely different values of this depending on how it is called. Let us look at all the ways this can shift.
Same function, three different callers
function introduce() {
console.log("Hello, I am " + this.name);
}
const person1 = { name: "Priya", introduce: introduce };
const person2 = { name: "Rahul", introduce: introduce };
const person3 = { name: "Ananya", introduce: introduce };
person1.introduce(); // Hello, I am Priya
person2.introduce(); // Hello, I am Rahul
person3.introduce(); // Hello, I am Ananya
introduce(); // Hello, I am undefined (or window.name in browser)
The exact same function produces four different results — all because of who called it.
call() — call with a specific this
call() lets you invoke a function and manually set what this should be:
function greet(greeting) {
console.log(greeting + ", I am " + this.name + " from " + this.city);
}
const person = { name: "Priya", city: "Mumbai" };
greet.call(person, "Hello");
// Hello, I am Priya from Mumbai
// ↑ this = person, "Hello" is the argument
apply() — like call() but arguments as an array
greet.apply(person, ["Namaste"]);
// Namaste, I am Priya from Mumbai
// ↑ same as call, but arguments passed as an array
bind() — create a new function with this permanently fixed
bind() does not call the function immediately. It returns a new function where this is permanently locked to the object you specify:
function greet() {
console.log("Hi, I am " + this.name);
}
const person = { name: "Priya" };
const boundGreet = greet.bind(person); // create a new function, this = person
boundGreet(); // Hi, I am Priya — this is always person, no matter how you call it
bind is useful when you need to pass a method as a callback but want to preserve its this:
const user = {
name: "Rahul",
greet: function() {
console.log("Hello from " + this.name);
}
};
// Without bind — this is lost when passed as a callback
setTimeout(user.greet, 1000); // Hello from undefined
// With bind — this is preserved
setTimeout(user.greet.bind(user), 1000); // Hello from Rahul
Summary — four ways this is determined
1. Called as a method obj.method() → this = obj
2. Called as a function fn() → this = global / undefined
3. Called with call/apply fn.call(obj) → this = obj (you choose)
4. Called with bind fn.bind(obj)() → this = obj (permanently fixed)
And the special case:
5. Arrow function () => { this } → this = inherited from lexical scope
Diagrams
Caller → Function Relationship
┌─────────────────────────────────────────────────────────────┐
│ CALLER → FUNCTION RELATIONSHIP │
│ │
│ const user = { │
│ name: "Priya", │
│ greet: function() { │
│ console.log(this.name); │
│ } │
│ }; │
│ │
│ ┌───────────────┐ calls ┌────────────────┐ │
│ │ user │ ─────────→ │ greet() │ │
│ │ ───────── │ │ ──────────── │ │
│ │ name: "Priya"│ │ this = user │ │
│ │ greet: fn │ │ this.name │ │
│ └───────────────┘ │ = "Priya" ✓ │ │
│ └────────────────┘ │
│ │
│ ───────────────────────────────────────────────────── │
│ │
│ Same function, different caller: │
│ │
│ ┌─────────────┐ ┌────────────────┐ │
│ │ person1 │ ─────────→ │ introduce() │ │
│ │ name:Priya │ calls │ this = person1│ │
│ └─────────────┘ │ → "Priya" │ │
│ └────────────────┘ │
│ │
│ ┌─────────────┐ ┌────────────────┐ │
│ │ person2 │ ─────────→ │ introduce() │ │
│ │ name:Rahul │ calls │ this = person2│ │
│ └─────────────┘ │ → "Rahul" │ │
│ └────────────────┘ │
│ │
│ ✦ Same function body — different this — different result │
│ ✦ this is set at CALL TIME, not at WRITE TIME │
└─────────────────────────────────────────────────────────────┘
Different Contexts of this
┌─────────────────────────────────────────────────────────────┐
│ DIFFERENT CONTEXTS OF `this` │
│ │
│ CONTEXT HOW CALLED this VALUE │
│ ─────────────────── ─────────────────── ────────────── │
│ │
│ Global scope (top-level code) window / global │
│ │
│ Object method obj.method() obj │
│ │
│ Plain function fn() window (or │
│ (non-strict) undefined strict) │
│ │
│ Arrow function () => {} inherited from │
│ enclosing scope │
│ │
│ call / apply fn.call(obj) obj (you set it) │
│ │
│ bind fn.bind(obj)() obj (locked in) │
│ │
│ ───────────────────────────────────────────────────── │
│ │
│ The lost `this` problem: │
│ │
│ const obj = { │
│ name: "Priya", │
│ start: function() { ← this = obj here ✓ │
│ setTimeout(function() { │
│ console.log(this.name); ← this = window ✗ │
│ }, 1000); ↑ │
│ } plain function callback — no caller object │
│ }; │
│ │
│ Fix: use arrow function inside the method │
│ setTimeout(() => { ← this inherited = obj ✓ │
│ console.log(this.name); ← "Priya" ✓ │
│ }, 1000); │
│ │
│ ✦ Arrow functions never have their own this │
│ ✦ They borrow this from where they were written │
└─────────────────────────────────────────────────────────────┘
Quick Reference
// ─── this in global context ───────────────────────────────
console.log(this); // window (browser) / global (Node.js)
// ─── this in an object method ─────────────────────────────
const user = {
name: "Priya",
greet() {
console.log(this.name); // "Priya" — this = user
}
};
user.greet();
// ─── this in a plain function ─────────────────────────────
function show() {
console.log(this); // window (non-strict) / undefined (strict)
}
show();
// ─── Arrow function inherits this ─────────────────────────
const obj = {
name: "Priya",
start() {
setTimeout(() => {
console.log(this.name); // "Priya" — arrow inherits from start()
}, 1000);
}
};
// ─── call() — invoke with specific this ───────────────────
function greet() { console.log(this.name); }
const person = { name: "Priya" };
greet.call(person); // "Priya"
// ─── apply() — like call but args as array ────────────────
greet.apply(person); // "Priya"
// ─── bind() — lock this permanently ──────────────────────
const boundGreet = greet.bind(person);
boundGreet(); // "Priya" — always
// ─── Summary ──────────────────────────────────────────────
// obj.method() → this = obj
// fn() → this = global / undefined
// fn.call(obj) → this = obj
// fn.bind(obj)() → this = obj
// () => {} → this = inherited
Wrapping Up
this stops being confusing the moment you anchor it to one question: "Who is calling this function?"
Here is everything you learned:
thisis a dynamic keyword — its value is set at the time of the function call, not when the function is writtenIn global context,
thisis thewindowobject (browser) orglobal(Node.js)In object methods,
thisis the object that owns and calls the methodIn plain functions,
thisis the global object (orundefinedin strict mode)Arrow functions never have their own
this— they inherit it from the surrounding scope, making them ideal for callbacks inside methodsYou can manually control
thisusingcall(),apply(), andbind()The most common bug: passing an object method as a callback and losing
this— fix it withbind()or an arrow function
Once you internalise "this is the caller", every case in this guide will make sense on first read — and debugging this-related bugs becomes straightforward.
Happy coding! 🚀
Next step: Practice by writing objects with multiple methods, calling them in different ways, and logging this each time. Five minutes of experimentation teaches more than an hour of reading.
