The this Keyword
Stop guessing why your methods are returning undefined and finally master the execution context.

At some point you will write a method on an object, call it directly and it works fine, then pass it somewhere as a callback and it completely falls apart. this.name comes back undefined. The code looks exactly the same to you. The error message is no help at all.
That is this doing something you did not expect, because nobody explained the one idea that makes all of its behavior obvious. This post is built around that idea. Everything else follows from it.
What this Represents
this is the object that called the function. Not where the function was written, not where it lives in the file. Who called it, right now, at this moment.
Think of a customer service line. When someone calls in, the rep picks up and sees the caller's name on screen. That display changes every time a different person rings. The phone line is the function. The name on the display is this.
Here is the simplest version of that in code:
function greet() {
console.log("Hello, I am " + this.name);
}
const user = { name: "Rohan", greet };
const admin = { name: "Priya", greet };
user.greet(); // user called it: this = user
admin.greet(); // admin called it: this = admin
// OUTPUT:
// Hello, I am Rohan
// Hello, I am Priya
One function, written once. The dot before the method call is your clue every time: whatever is on the left of that dot when the function runs is this. Keep that in mind as you read the rest of this post.
this In The Global Context
Before any functions or objects, there is already a this. At the very top of a script, outside everything else, it points to the global object. In a browser that is window.
// Browser
console.log(this); // Window {…}
console.log(this === window); // true
In Node.js the story is slightly different. At the top of a CommonJS file, this is the module's exports object, not Node's global. It looks like an empty object by default because nothing has been exported yet.
// Node.JS
console.log(this); // {}
console.log(this === global); // false
console.log(this === module.exports); // true
You will almost never use the global this on purpose. What matters here is knowing it exists so you are not confused when it shows up unexpectedly in the console or in an error message.
this Inside Objects
When a function is called as a method of an object, this is that object. This is the case that feels the most natural and the one that matches what beginners usually expect. The object called the method, so the object is this.
const player = {
name: "Rohan",
score: 1400,
showScore() {
console.log(this.name + " has " + this.score + " points");
}
};
player.showScore(); // player is on the left: this = player
// OUTPUT:
// Rohan has 1400 points
Swap the caller, swap this. The same function attached to two different objects gives two different results:
const playerOne = { name: "Rohan", score: 1400 };
const playerTwo = { name: "Divya", score: 2100 };
function showScore() {
console.log(this.name + " has " + this.score + " points");
}
playerOne.show = showScore;
playerTwo.show = showScore;
playerOne.show(); // this = playerOne
playerTwo.show(); // this = playerTwo
// OUTPUT:
// Rohan has 1400 points
// Divya has 2100 points
The Lost this
When you copy a method into a variable and call it, this is gone.
const player = {
name: "Rohan",
greet() {
console.log("Hi, I am " + this.name);
}
};
player.greet(); // `this` doesn't get lost
const fn = player.greet; // just the function, no object
fn(); // `this` gets lost
// OUTPUT:
// Hi, I am Rohan
// Hi, I am undefined
Passing a method as a callback is the same problem. setTimeout(player.greet, 1000) hands over the function with no object. When it runs a second later, this is not player. This shows up constantly in real code.
this Inside Regular Functions
When a plain function is called with nothing on the left of the dot, this defaults to the global object. In strict mode it becomes undefined instead, which is actually safer because it fails loudly.
// Non-strict. this defaults to Window
function whoAmI() {
console.log(this);
}
whoAmI(); // Window {…} in the browser
// Strict mode. this becomes undefined
"use strict";
function whoAmIStrict() {
console.log(this);
}
whoAmIStrict(); // undefined
ES modules are strict by default. If your files use import and export, strict mode is already on. Most modern JavaScript projects run in modules, so you already have this without thinking about it.
Nested Functions Lose this
A regular function written inside a method does not inherit the method's this. It resets. The inner function is called as a plain function, so it gets the global default. Proximity in code has no effect; only the call site matters.
const game = {
title: "Space Run",
start() {
console.log(this.title); // "Space Run". this = game
function inner() {
console.log(this.title); // undefined. this reset to Window
}
inner(); // called alone. no object on the left
}
};
game.start();
// OUTPUT:
// Space Run
// undefined
The fix for this is arrow functions, which do not reset this.
How the Calling Context Changes this
Because this is set at call time, you can control it. JavaScript gives you three methods for this: call, apply, and bind.
call() and apply()
Both invoke the function immediately and let you specify the object to use as this. The difference is just how you pass other arguments. call takes them one by one. apply takes an array.
function introduce(city, sport) {
console.log(this.name + " is from " + city + " and plays " + sport);
}
const maya = { name: "Maya" };
const leo = { name: "Leo" };
introduce.call(maya, "Mumbai", "cricket"); // args one by one
introduce.call(leo, "Lisbon", "football");
introduce.apply(maya, ["Mumbai", "cricket"]); // args as array
// OUTPUT
// Maya is from Mumbai and plays cricket
// Leo is from Lisbon and plays football
// Maya is from Mumbai and plays cricket
bind()
bind does not call the function. It returns a new copy with this permanently locked. That copy can be stored, passed around, and called later. It will always have the same this. This is the direct fix for the lost-this trap.
const player = {
name: "Rohan",
greet() {
console.log("Hi, I am " + this.name);
}
};
// Without bind, this is lost when passed as a callback
setTimeout(player.greet, 0);
// With bind, this is locked to player permanently
setTimeout(player.greet.bind(player), 0);
// OUTPUT:
// Hi, I am undefined
// Hi, I am Rohan
| Method | Calls immediately | Args format | Returns |
|---|---|---|---|
| call(obj, a, b) | Yes | one by one | result of the function |
| apply(obj, [a, b]) | Yes | as an array | result of the function |
| bind(obj, a, b) | No | one by one | a new bound function |
Arrow Functions and this
Arrow functions do not have their own this. They look at the code around them at the time they were written and borrow whatever this was in that scope. That value never changes, regardless of how the arrow function gets called later.
This is exactly what fixes the nested function problem:
const game = {
title: "Space Run",
start() {
// Regular function: this resets to Window inside here
function inner() {
console.log("regular: " + this.title);
}
// Arrow: borrows this from start(), which is game
const innerArrow = () => {
console.log("arrow: " + this.title);
};
inner();
innerArrow();
}
};
game.start();
// OUTPUT:
// regular: undefined
// arrow: Space Run
Where Arrow Functions Go Wrong
Arrow functions work great inside methods. Used as a method directly on an object, they break. The surrounding scope at write time is the global scope, so that is what gets borrowed.
// Arrow as method: breaks
const player = {
name: "Rohan",
greetArrow: () => {
// borrows global scope
console.log(this.name);
}
};
player.greetArrow();
// undefined
// Regular as method: works
const player = {
name: "Rohan",
greetRegular() {
// set at call time = player
console.log(this.name);
}
};
player.greetRegular();
// Rohan
The practical rule: write methods on objects and classes with regular function syntax. Write callbacks inside those methods with arrow functions. That combination gives you the right this in both places without needing to think too hard about it.
All the Contexts at Once
Every case in this post comes from the same question: who is calling the function right now? Here is how each calling pattern maps to a result, and how to recognise which one you are looking at:
When you are unsure which case applies, ask the same question every time: what is on the left of the dot when this function is called? If there is an object, that is this. If there is nothing, you are in the plain function case. If you used call, apply, or bind, you set it yourself. Arrow functions are the only exception, they ignore the call site entirely and use whatever was in scope when they were written.
Most of the bugs you will encounter are just the extracted-method case. A function that worked on an object, passed somewhere as a callback, lost its caller. bind fixes it. Arrow callbacks inside methods fix it too. Once you see what is happening, the fix is never complicated.
REFERENCES:




