Javascript 'this' Keyword

Hello friends, welcome to shrash studio learning, in this article we are going to untangle one of the trickiest and most asked topics in JavaScript — the this keyword. Developers commonly struggle with 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.

THE FOUR BINDING RULES FOR this

1. Default Binding

Standalone function call
greet();
Non-strict: global object
Strict: undefined

2. Implicit Binding

Method call on an object
user.greet();
this = the object before the dot

3. Explicit Binding

call / apply / bind
greet.call(user);
this = whatever you pass in

4. new Binding

Constructor call
new User();
this = freshly created object

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 Substitution: In non-strict mode only, when 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.

Finding the Value of this — A Decision Checklist
STEP 1: Is the function an arrow function?
If yes, this = the this of the surrounding function or scope. Stop here.
STEP 2: Was the function called with new?
If yes, this = the freshly created object. Stop here.
STEP 3: Was it called via call, apply, or a bound function?
If yes, this = whatever the first argument (or bound value) is. Stop here.
STEP 4: Was it called as a method (obj.fn())?
If yes, this = the object before the dot. Stop here.
STEP 5: Otherwise it is a standalone function call
Strict mode: this = undefined
Non-strict mode: this substitution kicks in; this = global object

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)

Tip: ES6 classes protect you from this mistake — calling them without 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

Interview Tip: When an interviewer asks "What is 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

Chakrapani U

Hi, I’m Chakrapani Upadhyaya, an IT professional with 15+ years of industry experience. Over the years, I have worked on web development, enterprise applications, database systems, and cloud-based solutions. Through this blog, I aim to simplify complex technical concepts and help learners grow from beginners to confident, industry-ready developers.

Previous Post Next Post

نموذج الاتصال