Skip to main content

Command Palette

Search for a command to run...

Understanding the this Keyword in JavaScript

Published
13 min read

Introduction

The this keyword is one of the most confusing concepts in JavaScript for beginners. It appears in almost every JavaScript program, but its meaning changes depending on where and how it's used. Understanding this is crucial because it affects how your code behaves, especially when working with objects and methods. In this blog, we'll demystify the this keyword by exploring what it represents, how it changes based on context, and how to use it effectively in your code.


1. What Does this Represent?

The this keyword in JavaScript represents the object that is executing the current function. Think of this as a way for a function to know who called it.

Simple Analogy:

Imagine you're a teacher and you ask a student "What's your name?" The student who hears the question answers with their own name. In this case, "you" (the question) is like this - it refers to whoever is being addressed at that moment.

In JavaScript, this refers to the object that is currently calling or executing the function.

A Basic Example:

const person = {
  name: "Isha",
  greet: function() {
    console.log("Hello, I am " + this.name);
  }
};

person.greet(); // Output: Hello, I am Isha

Here, this refers to the person object because person is calling the greet function. The greet function doesn't inherently know its own name, but this tells it "you are being called by the person object."

Key Concept:

this is determined by how a function is called, not by where the function is defined.

const person1 = {
  name: "Raj",
  introduce: function() {
    return "I am " + this.name;
  }
};

const person2 = {
  name: "Priya"
};

// Same function, different callers
console.log(person1.introduce()); // "I am Raj"

// Now assign the function to person2
person2.introduce = person1.introduce;
console.log(person2.introduce()); // "I am Priya"

The same function behaves differently depending on who calls it. That's the power of this.


2. this in the Global Context

When this is used outside of any object or function, it refers to the global object.

In Browsers:

The global object is called window.

console.log(this); // window object

var globalVar = "I am global";

console.log(this.globalVar); // "I am global"
console.log(window.globalVar); // "I am global"

When you declare a variable with var at the top level (not inside a function or object), it becomes a property of the global object.

In Node.js:

The global object is called global.

console.log(this); // global object

var globalVar = "I am global";

console.log(this.globalVar); // "I am global"
console.log(global.globalVar); // "I am global"

A Function in Global Context:

function displayThis() {
  console.log(this);
}

displayThis(); // window (in browser) or global (in Node.js)

When a function is called without an object before it, this refers to the global object.

Important Note:

If you use const or let instead of var, the variable is not added to the global object:

let globalLet = "With let";
var globalVar = "With var";

console.log(this.globalLet); // undefined
console.log(this.globalVar); // "With var"

This is one reason why modern JavaScript prefers let and const over var.


3. this Inside Objects

When a function is called as a method of an object, this refers to that object.

Basic Object Method:

const car = {
  brand: "Toyota",
  model: "Fortuner",
  
  displayInfo: function() {
    console.log(this.brand + " " + this.model);
  }
};

car.displayInfo(); // Output: Toyota Fortuner

Here, this refers to the car object because car.displayInfo() is calling the function.

Accessing Multiple Properties:

const student = {
  name: "Arjun",
  age: 20,
  city: "Bangalore",
  
  introduce: function() {
    console.log("My name is " + this.name);
    console.log("I am " + this.age + " years old");
    console.log("I live in " + this.city);
  }
};

student.introduce();
// Output:
// My name is Arjun
// I am 20 years old
// I live in Bangalore

Modifying Object Properties:

const bank = {
  balance: 5000,
  
  deposit: function(amount) {
    this.balance = this.balance + amount;
    console.log("New balance: Rs." + this.balance);
  },
  
  withdraw: function(amount) {
    this.balance = this.balance - amount;
    console.log("New balance: Rs." + this.balance);
  }
};

bank.deposit(2000);   // New balance: Rs.7000
bank.withdraw(1500);  // New balance: Rs.5500

Notice how this.balance refers to the balance property of the bank object. Without this, the function wouldn't know which object's balance to update.

Nested Objects:

const company = {
  name: "Tech Solutions",
  
  department: {
    name: "Engineering",
    
    getInfo: function() {
      console.log("Department: " + this.name); // Refers to department object
    }
  }
};

company.department.getInfo(); // Output: Department: Engineering

Important: this refers to the immediate parent object (the one directly calling the method), not the outer object.


4. this Inside Functions

Functions can be called in different ways, and the value of this depends on how they're called.

Function Called Without an Object:

function sayMyName() {
  console.log("My name is: " + this.name);
}

var name = "Global Name";
sayMyName(); // Output: My name is: Global Name

When a function is called without an object, this refers to the global object (window in browsers, global in Node.js).

Function Called with an Object:

const person = {
  name: "Deepak",
  greet: sayMyName
};

function sayMyName() {
  console.log("My name is: " + this.name);
}

person.greet(); // Output: My name is: Deepak

Now this refers to the person object because person.greet() called the function.

The Problem: Losing Context

This is where things get tricky. When you store a function in a variable, it can lose its context:

const phone = {
  brand: "Samsung",
  
  getBrand: function() {
    return this.brand;
  }
};

console.log(phone.getBrand()); // "Samsung"

// Store the function in a new variable
const myFunction = phone.getBrand;

console.log(myFunction()); // undefined or "Global Brand" (if exists)

Why? Because myFunction() is called without an object, so this is the global object, not the phone object.

Passing Functions as Arguments:

const restaurant = {
  name: "Spice House",
  
  greetCustomer: function() {
    console.log("Welcome to " + this.name);
  }
};

// This works
restaurant.greetCustomer(); // Welcome to Spice House

// This doesn't work
setTimeout(restaurant.greetCustomer, 1000); 
// Output: Welcome to undefined
// Because setTimeout calls the function, not the restaurant object

The Solution: Arrow Functions

Arrow functions have special behavior with this. They don't have their own this. Instead, they inherit this from the surrounding code:

const restaurant = {
  name: "Spice House",
  
  greetCustomer: function() {
    // Regular function
    console.log("Welcome to " + this.name);
  },
  
  greetWithArrow: () => {
    // Arrow function - inherits this from outer scope
    console.log("Welcome to " + this.name);
  }
};

restaurant.greetCustomer();   // Welcome to Spice House
restaurant.greetWithArrow();  // Welcome to undefined

Arrow functions are useful in callbacks where you want to preserve the this value from the outer function.


5. How Calling Context Changes this

The most important rule: this is determined by how the function is called, not where it's defined.

Rule 1: Method Call (Object Calling the Function)

const user = {
  username: "vikram_007",
  
  login: function() {
    console.log(this.username + " logged in");
  }
};

user.login(); // this = user
// Output: vikram_007 logged in

When you use object.method(), this is the object.

Rule 2: Function Call (No Object)

function login() {
  console.log(this.username + " logged in");
}

login(); // this = global object
// Output: undefined logged in

When you call a function directly, this is the global object.

Rule 3: Using the call() Method

The call() method lets you explicitly specify what this should be:

function introduce() {
  console.log("Hello, I am " + this.name);
}

const person1 = { name: "Neha" };
const person2 = { name: "Arjun" };

introduce.call(person1); // this = person1, Output: Hello, I am Neha
introduce.call(person2); // this = person2, Output: Hello, I am Arjun

Rule 4: Using the apply() Method

apply() is similar to call(), but you pass arguments as an array:

function introduce(greeting, hobby) {
  console.log(greeting + ", I am " + this.name + " and I like " + hobby);
}

const person = { name: "Priya" };

introduce.apply(person, ["Hello", "coding"]);
// Output: Hello, I am Priya and I like coding

Rule 5: Using the bind() Method

bind() creates a new function with this permanently set:

function greet() {
  console.log("Hi, I am " + this.name);
}

const person = { name: "Rohan" };

const boundGreet = greet.bind(person);

boundGreet(); // Output: Hi, I am Rohan

// Even if you call it without an object, this still works
const anotherRef = boundGreet;
anotherRef(); // Output: Hi, I am Rohan

bind() is very useful when passing functions as callbacks, because it locks in the this value.

Practical Example: Event Handlers

const button = {
  label: "Click Me",
  
  handleClick: function() {
    console.log(this.label + " was clicked");
  }
};

// This won't work - this will be the button element, not our object
// document.getElementById("myBtn").addEventListener("click", button.handleClick);

// Solution 1: Use bind()
document.getElementById("myBtn").addEventListener("click", button.handleClick.bind(button));

// Solution 2: Use an arrow function (which preserves this)
document.getElementById("myBtn").addEventListener("click", () => {
  button.handleClick();
});

6. Common this Mistakes and How to Avoid Them

Mistake 1: Losing Context When Storing a Method

const game = {
  score: 0,
  
  increaseScore: function() {
    this.score++;
    console.log("Score: " + this.score);
  }
};

game.increaseScore(); // Works: Score: 1

const fn = game.increaseScore;
fn(); // Broken: this is undefined or global object

Solution: Use bind()

const fn = game.increaseScore.bind(game);
fn(); // Works: Score: 2

Mistake 2: this in Nested Functions

const user = {
  name: "Maya",
  
  init: function() {
    console.log(this.name); // "Maya" - correct
    
    function innerFunction() {
      console.log(this.name); // undefined - wrong!
    }
    
    innerFunction();
  }
};

user.init();

Solution: Use an arrow function or save this

const user = {
  name: "Maya",
  
  init: function() {
    console.log(this.name); // "Maya"
    
    // Solution 1: Arrow function
    const inner1 = () => {
      console.log(this.name); // "Maya" - correct
    };
    inner1();
    
    // Solution 2: Save this
    const self = this;
    function inner2() {
      console.log(self.name); // "Maya" - correct
    }
    inner2();
  }
};

user.init();

Mistake 3: Arrow Functions in Object Methods

const car = {
  brand: "BMW",
  
  // Wrong: Arrow function doesn't get this from the object
  displayBrand: () => {
    console.log(this.brand); // undefined
  },
  
  // Correct: Regular function gets this from the object
  displayBrandCorrect: function() {
    console.log(this.brand); // "BMW"
  }
};

car.displayBrand(); // undefined
car.displayBrandCorrect(); // "BMW"

Rule: Use arrow functions for callbacks, regular functions for object methods.


7. this in Different Scenarios

In Constructor Functions:

function Person(name, age) {
  this.name = name;
  this.age = age;
  
  this.greet = function() {
    console.log("Hi, I am " + this.name);
  };
}

const p1 = new Person("Arjun", 25);
const p2 = new Person("Priya", 23);

p1.greet(); // Hi, I am Arjun
p2.greet(); // Hi, I am Priya

When using new, this refers to the newly created object.

In Classes:

class Employee {
  constructor(name, salary) {
    this.name = name;
    this.salary = salary;
  }
  
  getSalaryInfo() {
    return this.name + " earns Rs." + this.salary;
  }
}

const emp = new Employee("Vikram", 50000);
console.log(emp.getSalaryInfo()); // Vikram earns Rs.50000

In classes, this works similarly to constructor functions.

In Array Methods with Callbacks:

const numbers = [1, 2, 3, 4, 5];

// Using regular function
numbers.forEach(function(num) {
  console.log(this); // global object (or window)
});

// Using arrow function
const obj = {
  multiplier: 2,
  
  multiplyNumbers: function() {
    numbers.forEach((num) => {
      console.log(this.multiplier); // Refers to obj (correct)
    });
  }
};

obj.multiplyNumbers();

8. Visual Guide: How this Changes

Here's a quick reference for determining this:

METHOD CALLED AS:          VALUE OF this:
===============================================
object.method()            the object
function()                 global object
obj1.obj2.method()         obj2 (immediate parent)
method.call(obj, ...)      obj (explicitly set)
method.apply(obj, [...])   obj (explicitly set)
method.bind(obj)           obj (locked in)
new Constructor()          the new object
arrow => {}                inherited from outer scope

Examples:

const config = {
  debug: true,
  log: function() {
    console.log(this.debug); // true
  }
};

// Method call
config.log(); // this = config

// Function call
const fn = config.log;
fn(); // this = global object

// Using call
fn.call(config); // this = config

// Using bind
const boundFn = fn.bind(config);
boundFn(); // this = config

9. Practical Real-World Example

Let's build a practical example that combines all these concepts:

const account = {
  accountNumber: "1234567890",
  balance: 10000,
  owner: "Rajesh Kumar",
  
  // Method to display account info
  getInfo: function() {
    console.log("Account: " + this.accountNumber);
    console.log("Owner: " + this.owner);
    console.log("Balance: Rs." + this.balance);
  },
  
  // Method to deposit money
  deposit: function(amount) {
    this.balance += amount;
    console.log("Deposited Rs." + amount);
    console.log("New balance: Rs." + this.balance);
  },
  
  // Method to withdraw money
  withdraw: function(amount) {
    if (amount <= this.balance) {
      this.balance -= amount;
      console.log("Withdrawn Rs." + amount);
      console.log("New balance: Rs." + this.balance);
    } else {
      console.log("Insufficient balance");
    }
  },
  
  // Method to set up automatic transactions
  setupAutoTransaction: function() {
    // Using arrow function to preserve this
    const auto = () => {
      console.log("Auto transaction for " + this.owner);
      this.deposit(1000);
    };
    
    // Simulate auto transaction after 2 seconds
    setTimeout(auto, 2000);
  }
};

// Using the account
account.getInfo();
// Output:
// Account: 1234567890
// Owner: Rajesh Kumar
// Balance: Rs.10000

account.deposit(5000);
// Output:
// Deposited Rs.5000
// New balance: Rs.15000

account.withdraw(3000);
// Output:
// Withdrawn Rs.3000
// New balance: Rs.12000

account.setupAutoTransaction();
// Output (after 2 seconds):
// Auto transaction for Rajesh Kumar
// Deposited Rs.1000
// New balance: Rs.13000

10. Debugging this

If you're confused about what this is in your code, here's how to debug it:

function myFunction() {
  console.log("this is:", this);
  console.log("this.name is:", this.name);
}

const obj = { name: "TestObject" };

myFunction(); // Log this to see what it is
obj.myFunction = myFunction;
obj.myFunction(); // Log this again

Pro Tip: Use console.log(this) or add debugger statements to see exactly what this refers to in any situation.


Conclusion

The this keyword is one of the most important concepts in JavaScript, but it's not magic. Just remember these key points:

  1. this refers to the object that is calling the function

  2. In global context, this is the global object (window or global)

  3. Inside object methods, this is the object

  4. this is determined by how the function is called, not where it's defined

  5. Use call(), apply(), or bind() to explicitly set this

  6. Arrow functions don't have their own this, they inherit it from the surrounding scope

Understanding this will make you a better JavaScript developer. It's used everywhere in real-world code, so investing time to understand it now will pay off tremendously in the future. Start practicing with simple examples, and gradually build up to more complex scenarios.