this because its value shifts depending on where and how it is used. We will carefully walk through every important situation — the global scope, regular functions in both strict and non-strict mode, the concept of this substitution, object methods, arrow functions and their lexical this, the call, apply, and bind methods, DOM event handlers, and a quick look at classes and constructors. By the end, you will know exactly what this will be in any piece of JavaScript code you encounter — whether in your own projects, in legacy code, or in tough interview questions.
What is the this Keyword in JavaScript
In JavaScript, this is a special keyword that automatically refers to an object — but which object it refers to is decided at the moment the function runs, not when it is written. That is what makes it so powerful and, at the same time, so confusing. Unlike a regular variable that you declare and assign yourself, this is silently bound by the engine based on a few clear rules that depend on the surrounding context.
You can think of this as a word whose meaning depends entirely on the sentence it appears in. In the global scope it means one thing. Inside a normal function it means something else. Inside a method called on an object it points to that object. Inside an arrow function it has no meaning of its own — it borrows it from wherever the arrow was written. Once you understand these context rules, this stops feeling like magic and starts feeling like a consistent set of clearly-defined behaviors.
The golden rule worth repeating before we go into any detail is this: the value of this is determined by how a function is called, not by how or where it is defined. Forget this rule and every example will look random; remember it and all the rules fit together cleanly.
Why the this Keyword Exists
The this keyword exists so that a single function can work with different objects without having to hard-code them. Imagine you have a function that prints a user's name. If you had to write that function separately for every user object, your code would explode in size. With this, you write the function once and let the engine decide which object it applies to based on how you call it. That flexibility is the entire reason this was added to the language.
this also makes it possible for objects and classes to have methods that naturally reference their own properties. When you write user.greet(), the greet method can refer to this.name to get the user's name — no matter which specific user it happens to be. This is how almost every object-oriented pattern in JavaScript works, from plain object literals to ES6 classes to prototypal inheritance.
Finally, this is what makes call, apply, and bind possible. These methods let you explicitly set the value of this at call time, giving you fine-grained control over which object a function should operate on. This technique is invaluable when you need to borrow a method from one object and apply it to another without duplicating the code.
Internal Working: How this Is Bound
To understand this systematically, it helps to memorize the four binding rules that JavaScript uses to decide what this means. Every function call in JavaScript falls into exactly one of these categories, and the category completely determines the final value.
1. Default Binding
Strict: undefined
2. Implicit Binding
3. Explicit Binding
4. new Binding
Arrow functions deliberately ignore all four of these rules. They do not have their own this at all. Instead, they inherit the this value from the lexical environment they were created in — that is, the function or scope that physically surrounds them in the source code. This is the single most important thing to remember about arrow functions.
Practical Examples of this in Every Context
Let us now explore every important scenario one by one, with complete code snippets you can run in your own browser console.
Example 1: this in the Global Scope
console.log(this);
// In a browser: Window { ... }
// In Node.js (at top level of a module): {}
// In Node.js REPL: the global object
At the top level of a script, this always points to the runtime's global object. In the browser this is the window object; in Node.js it depends on whether you are inside a module (where it is an empty object) or the REPL (where it is the global object). The takeaway is simple — in the global scope, this equals whatever the platform calls its global.
Example 2: this Inside a Regular Function (Non-Strict Mode)
function showThis() {
console.log(this);
}
showThis();
// Non-strict mode output (browser): Window { ... }
When a function is called with no object in front of it (a standalone call), this defaults to undefined. In non-strict mode, JavaScript applies a special substitution — it silently replaces undefined with the global object. That is why the result appears to be window in the browser.
Example 3: this Inside a Regular Function (Strict Mode)
"use strict";
function showThis() {
console.log(this);
}
showThis();
// Strict mode output: undefined
In strict mode, the substitution does not happen. this stays exactly what it was — undefined. This strict behavior is safer because it prevents functions from accidentally leaking into the global object. ES6 modules and class bodies are automatically in strict mode, so you will see this behavior by default in modern code.
this would be undefined or null, JavaScript replaces it with the global object. This single rule explains why the same function seems to behave differently across codebases — the difference is whether strict mode is active.
Example 4: this Inside an Object Method (Implicit Binding)
const book = {
title: "Atomic Habits",
pages: 320,
describe: function () {
console.log(this.title + " has " + this.pages + " pages");
}
};
book.describe();
// Output: Atomic Habits has 320 pages
When you call a function as a method of an object (using the dot syntax), this is automatically set to that object — the one immediately to the left of the dot. Inside describe, this.title and this.pages refer to the book object's own properties.
Example 5: Losing this When a Method Is Detached
const book = {
title: "Atomic Habits",
describe: function () {
console.log(this.title);
}
};
const fn = book.describe;
fn();
// Strict mode: TypeError — cannot read title of undefined
// Non-strict mode: undefined (this becomes window, window.title is undefined)
This is one of the most common sources of bugs. The moment you pull a method out of its object and call it on its own, the implicit binding is lost. fn() is now a standalone call, so this reverts to the default binding. To keep the intended object, you must use bind, an arrow wrapper, or call the method through the object again.
Example 6: Using call, apply, and bind (Explicit Binding)
function introduce(city, role) {
console.log(this.name + " from " + city + " works as a " + role);
}
const person1 = { name: "Rahul" };
const person2 = { name: "Priya" };
introduce.call(person1, "Delhi", "designer");
// Output: Rahul from Delhi works as a designer
introduce.apply(person2, ["Mumbai", "developer"]);
// Output: Priya from Mumbai works as a developer
const boundFn = introduce.bind(person1, "Bengaluru", "architect");
boundFn();
// Output: Rahul from Bengaluru works as an architect
All three methods let you take a standalone function and force its this to be whatever you want. call accepts arguments one by one. apply takes arguments as an array. bind does not invoke the function — it returns a brand-new function whose this is permanently locked to the value you provided. These three tools give you complete manual control over the binding.
Example 7: this Inside an Arrow Function
const book = {
title: "Atomic Habits",
describe: () => {
console.log(this); // lexical this, NOT the book
console.log(this.title); // undefined
}
};
book.describe();
// Output (in a module / strict mode): undefined then undefined
This is the classic arrow function surprise. Even though describe is called as a method, the arrow function does not get its own this. It inherits from wherever the arrow was written, which in this case is the module-level scope. Never use an arrow function as an object method unless you deliberately want to inherit the outer this.
Example 8: The Correct Use of Arrow Functions — Inside Methods
const timer = {
seconds: 0,
start: function () {
setInterval(() => {
this.seconds++; // arrow inherits timer
console.log(this.seconds);
}, 1000);
}
};
timer.start();
// Output: 1, 2, 3, 4, ...
This is where arrow functions truly shine. Because the arrow callback inside setInterval does not have its own this, it picks up the this of the surrounding start method — which is timer. If you had used a regular function expression here, this.seconds would have been undefined or a global leak. Arrow functions eliminate that entire class of bugs.
Example 9: this Inside DOM Event Handlers
// In the HTML:
// <button id="saveBtn">Save</button>
const btn = document.getElementById("saveBtn");
btn.addEventListener("click", function () {
console.log(this); // the button element
console.log(this.tagName); // "BUTTON"
this.disabled = true; // disable itself after click
});
// Arrow version would inherit outer this (usually window/module), not the button:
// btn.addEventListener("click", () => console.log(this)); // NOT the button
When a regular function is used as a DOM event handler, this is set to the element that received the event — in this case, the button. This makes it easy to manipulate the element from inside the handler without searching for it again. Switching to an arrow function would break this convenient binding, which is why you usually want a regular function for event handlers that need the element reference.
Example 10: this Inside Classes and Constructors
class Book {
constructor(title, pages) {
this.title = title;
this.pages = pages;
}
describe() {
console.log(this.title + " has " + this.pages + " pages");
}
}
const atomic = new Book("Atomic Habits", 320);
atomic.describe();
// Output: Atomic Habits has 320 pages
Inside a class constructor called with new, JavaScript creates a brand-new empty object and binds this to it. The constructor attaches properties to this, and the newly populated object becomes the result of the expression. Methods defined on the class use implicit binding when called through an instance.
Output Summary for Each Context
Here is a consolidated reference showing what this resolves to in every common situation.
| Where | Strict Mode | Non-Strict Mode |
|---|---|---|
| Global scope | Global object (window / globalThis) | Global object (window / globalThis) |
| Standalone function call | undefined | Global object (via this substitution) |
| Object method (obj.fn()) | The object | The object |
| Detached method (fn = obj.fn; fn()) | undefined | Global object |
| call / apply / bind | Whatever you pass in | Whatever you pass in |
| Constructor (new Fn()) | Newly created object | Newly created object |
| Arrow function | Enclosing lexical this | Enclosing lexical this |
| DOM event handler (regular fn) | The DOM element | The DOM element |
| Class method | The instance (when called via instance) | The instance (when called via instance) |
Step-by-Step: How to Determine this for Any Code
Use this mental checklist whenever you see this in unfamiliar code and need to figure out what it resolves to.
Follow this checklist in order. The first rule that applies wins. This simple procedure correctly resolves every this in every piece of JavaScript code you will ever read.
Common Mistakes Developers Make with this
Even experienced developers get tripped up by this. Here are the most frequent mistakes and how to avoid them.
Mistake 1: Using Arrow Functions as Object Methods
As we saw earlier, arrow functions do not have their own this, so they cannot serve as methods that rely on the owning object. Always use regular function expressions (or the shorthand method syntax) for object methods that reference this.
// Wrong
const user = {
name: "Rahul",
greet: () => console.log("Hi, " + this.name)
};
user.greet(); // "Hi, undefined"
// Right
const user2 = {
name: "Rahul",
greet() { console.log("Hi, " + this.name); }
};
user2.greet(); // "Hi, Rahul"
Mistake 2: Losing this When Passing Methods as Callbacks
const counter = {
value: 0,
increment() { this.value++; console.log(this.value); }
};
setTimeout(counter.increment, 1000);
// Wrong: this is no longer 'counter' when setTimeout calls it
setTimeout(counter.increment.bind(counter), 1000);
// Right: bind locks this to counter
setTimeout(() => counter.increment(), 1000);
// Also right: arrow preserves the method call via the arrow's body
Mistake 3: Forgetting new Inside a Constructor
If you call a constructor function without new, this does not refer to a new object. In non-strict mode it silently becomes the global object, which pollutes globals in dangerous ways.
function Product(name) { this.name = name; }
const p = Product("Pen"); // forgot 'new'
console.log(p); // undefined
console.log(window.name); // "Pen" (global leak!)
const p2 = new Product("Pen");
console.log(p2.name); // "Pen" (correct)
new throws a TypeError immediately. Prefer classes over old constructor functions in new code.
Mistake 4: Assuming this Inside Nested Functions Is Preserved
When you nest a regular function inside a method, the inner function's this does not carry over. The inner function is a standalone call, so it gets the default binding.
const obj = {
value: 42,
outer() {
function inner() {
console.log(this.value); // undefined in strict mode
}
inner();
}
};
obj.outer();
// Fix with an arrow (preferred)
const obj2 = {
value: 42,
outer() {
const inner = () => console.log(this.value);
inner();
}
};
obj2.outer(); // 42
Comparison: Regular Functions vs Arrow Functions for this
| Aspect | Regular Function | Arrow Function |
|---|---|---|
| Has its own this? | Yes | No |
| How this is determined | By how the function is called | Inherited from the enclosing scope |
| Affected by call / apply / bind? | Yes | No (they are ignored) |
| Usable as a constructor with new? | Yes | No — throws TypeError |
| Good choice for object methods? | Yes | No (loses the object reference) |
| Good choice for inner callbacks in a method? | No (loses outer this) | Yes (inherits outer this) |
| Has its own arguments object? | Yes | No (must use rest parameters) |
Real-World Usage of the this Keyword
Understanding this is not just academic — it lets you write cleaner object-oriented code, reusable utilities, and bug-free event handlers.
Scenario 1: Reusing a Method Across Different Objects
function summarize() {
return this.title + " (" + this.year + ")";
}
const movie1 = { title: "Inception", year: 2010 };
const movie2 = { title: "Interstellar", year: 2014 };
console.log(summarize.call(movie1)); // "Inception (2010)"
console.log(summarize.call(movie2)); // "Interstellar (2014)"
Scenario 2: Partial Application with bind
function multiply(a, b) { return a * b; }
const double = multiply.bind(null, 2);
const triple = multiply.bind(null, 3);
console.log(double(10)); // 20
console.log(triple(10)); // 30
Scenario 3: Writing Chainable APIs
class QueryBuilder {
constructor() { this.parts = []; }
select(cols) { this.parts.push("SELECT " + cols); return this; }
from(table) { this.parts.push("FROM " + table); return this; }
where(cond) { this.parts.push("WHERE " + cond); return this; }
build() { return this.parts.join(" "); }
}
const query = new QueryBuilder()
.select("id, name")
.from("users")
.where("active = 1")
.build();
console.log(query);
// SELECT id, name FROM users WHERE active = 1
Each method returns this so the next method in the chain can keep working on the same object. This is the foundation of fluent APIs you see in libraries like jQuery, D3, and many ORMs.
Scenario 4: Event Handler That Edits Its Own Element
const inputs = document.querySelectorAll("input[type='text']");
inputs.forEach(function (input) {
input.addEventListener("focus", function () {
this.style.borderColor = "#0f3460";
});
input.addEventListener("blur", function () {
this.style.borderColor = "";
});
});
Scenario 5: Safe Callback Inside a Class
class Stopwatch {
constructor() {
this.seconds = 0;
}
start() {
this.id = setInterval(() => {
this.seconds++;
console.log(this.seconds);
}, 1000);
}
stop() {
clearInterval(this.id);
}
}
const s = new Stopwatch();
s.start();
// Logs 1, 2, 3, ... correctly thanks to the arrow callback
this?", do not just say "the current object". Walk through the four binding rules and the arrow-function exception. Mention strict-mode behavior and the this substitution rule for non-strict code. That level of clarity signals a mature JavaScript developer.
Summary
The this keyword in JavaScript refers to an object whose identity is determined by how a function is called, not how it is defined. In the global scope, this points to the runtime's global object — window in browsers, globalThis everywhere. Inside a standalone function call, this is undefined in strict mode and the global object in non-strict mode, thanks to the this substitution rule that silently replaces undefined with the global.
When a function is called as an object method using the dot syntax, this becomes that object. When a function is called via call, apply, or through a function produced by bind, the this value is whatever you explicitly passed in. When a function is called with new, this is the freshly created object. These four binding rules — default, implicit, explicit, and new — cover every regular function call you will ever see.
Arrow functions deliberately step outside these rules. They do not have their own this; they inherit it from the enclosing lexical scope. This makes them perfect for inner callbacks inside object methods and class methods, where you usually want to preserve the outer this, but it makes them a poor choice as standalone object methods. DOM event handlers written as regular functions automatically bind this to the element receiving the event, which is why event handlers with arrow functions do not get the element reference.
When you combine these rules — the four binding types, the arrow function exception, strict versus non-strict behavior, and the this substitution rule — you have everything you need to predict the value of this in any piece of code. Use the five-step decision checklist above whenever you are unsure, and you will never be caught off-guard by this again, whether in daily coding, tricky interview puzzles, or reading someone else's unfamiliar codebase.
| Concept | Key Takeaway |
|---|---|
| Golden Rule | this depends on how a function is called, not where it is defined |
| Global this | The global object (window / globalThis) |
| Standalone call | undefined in strict mode, global object in non-strict (this substitution) |
| Method call | The object before the dot |
| call / apply / bind | Explicit this — whatever you pass |
| Constructor with new | A brand-new empty object |
| Arrow function | Inherits this from the surrounding scope — no own binding |
| DOM event handler (regular fn) | The element that received the event |
| Detached method | Loses its implicit binding — use bind or an arrow wrapper |