JavaScript This Keyword and Binding: Complete Guide with Call, Apply, and Bind Methods

JavaScript This Keyword and Binding: Complete Guide with Call, Apply, and Bind Methods

In this article, we will learn what is 'this' keyword in javascript.

What is This keyword in javascript?

This keyword refers to an object, that object which is executing the current bit of javascript code. 

In other words, every javascript function while executing has a reference to its current execution context, called this. Execution context means here is how the function is called.

Basically, The javascript this keyword refers to the object it belongs to.

It has different values depending on where it is used:

Global Context

 Alone, this refers to the global object,i.e: The window the object on the web browser

console.log(this);

// Window {window: Window, self: Window, document: document, name: '', location: Location, …}

This in regular functions

In a regular function with a non-strict mode, this refers to the global object.

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

  hello();

// Window {window: Window, self: Window, document: document, name: '', location: Location, …}

The above function will return the windows object in the console.

But, this inside a function in strict mode will give undefined.

"use strict";
  
function hello(){
    console.log(this);
  }

  hello();

// undefined

This in Methods

In a method, this refers to the owner object, in this case, is user object

  const user = {
    name: "jigar",
    language: "English",
    greet: function () {
      console.log(`${this.name} knows ${this.language}`);
    },
  };

Now let’s execute the greet method.

 user.greet();

This prints:

jigar knows English

  When we are calling the greet() method using the user object, so the this keyword inside the method refers to the user object.  

This in Arrow functions

The Arrow function does not create its own execution context, it inherits the value of this from the outer function

So, If you try to access this in arrow function then it searches for the outer function and picks the value of this, if there is no outer function then this will be window object.

const arrowFunction=()=>{
    console.log(this);
}
arrowFunction();
// Window {window: Window, self: Window, document: document, name: '', location: Location, …}

In the above example, it checks for outer function, and as there is no outer function, so it will be a window object.

const user = {
    name: "jigar",
    language: "English",
    getName:()=>{
        console.log(this);//Window
        console.log(this.name); // undefined
    }  
  };

user.getName();

Since there is no outer function, it will be a window object as well.

const user = {
    name: "jigar",
    language: "English",
    getName:function(){
        return ()=>{
            console.log(this);
        }
    }  
  };

user.getName()();

// {name: 'jigar', language: 'English', getName: ƒ}

In the above example, it tries to check with the outer function and it finds getName and inherits this value of the getName function.

Understanding JavaScript's this keyword is like mastering the art of context in conversation. Just as the word "this" in English can refer to different things depending on the situation, JavaScript's this keyword changes its meaning based on how and where it's used. Whether you're a beginner struggling with function context or an experienced developer looking to deepen your understanding, this comprehensive guide will transform your approach to JavaScript binding.

What is the 'this' Keyword in JavaScript?

The this keyword in JavaScript is a special identifier that refers to the object that is currently executing the code. Think of it as a pronoun that points to different objects depending on the context of its use. Unlike other programming languages where this always refers to the current instance, JavaScript's this is dynamic and context-dependent.

Why is Understanding 'this' So Important?

Before diving into the technical details, let's understand why mastering this is crucial:

  • Object-Oriented Programming: Essential for creating and managing object methods
  • Event Handling: Critical for understanding DOM event callbacks
  • Framework Development: Fundamental for React, Vue, and Angular development
  • Code Maintainability: Prevents common bugs and unexpected behavior

Different Contexts of 'this' in JavaScript

1. Global Context

In the global execution context, this refers to the global object:

// In browsers, global 'this' refers to the window object
console.log(this); // Window object (in browsers)

// In Node.js, it refers to the global object
console.log(this === global); // true (in Node.js)

Real-world example: When you accidentally create global variables without proper scoping:

function calculateTax() {
    this.taxRate = 0.08; // Accidentally creates a global variable
    return this.taxRate;
}

calculateTax(); // 0.08
console.log(window.taxRate); // 0.08 (global pollution!)

2. Function Context

When this is used inside a regular function, its value depends on how the function is called:

function greetUser() {
    console.log(this); // Window object (in non-strict mode)
}

greetUser(); // 'this' refers to the global object

In strict mode, this is undefined:

'use strict';
function greetUser() {
    console.log(this); // undefined
}

greetUser();

3. Method Context

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

const restaurant = {
    name: 'Pizza Palace',
    location: 'Downtown',
    
    getInfo() {
        return `${this.name} is located in ${this.location}`;
    }
};

console.log(restaurant.getInfo()); // "Pizza Palace is located in Downtown"

Real-world use case: Building a shopping cart system:

const shoppingCart = {
    items: [],
    total: 0,
    
    addItem(item) {
        this.items.push(item);
        this.total += item.price;
        console.log(`Added ${item.name} to cart. Total: $${this.total}`);
    },
    
    getItemCount() {
        return this.items.length;
    }
};

shoppingCart.addItem({ name: 'Laptop', price: 999 });
// "Added Laptop to cart. Total: $999"

4. Constructor Context

When a function is used as a constructor with the new keyword, this refers to the newly created instance:

function BankAccount(accountNumber, initialBalance) {
    this.accountNumber = accountNumber;
    this.balance = initialBalance;
    
    this.deposit = function(amount) {
        this.balance += amount;
        console.log(`Deposited $${amount}. New balance: $${this.balance}`);
    };
}

const myAccount = new BankAccount('123456789', 1000);
myAccount.deposit(500); // "Deposited $500. New balance: $1500"

Understanding Call, Apply, and Bind Methods

These three methods allow you to explicitly control what this refers to in your functions. Think of them as remote controls for function context.

The call() Method

The call() method invokes a function with a specific this value and arguments passed individually:

function introduce(greeting, punctuation) {
    console.log(`${greeting}, I'm ${this.name}${punctuation}`);
}

const person = { name: 'Alice' };
introduce.call(person, 'Hello', '!'); // "Hello, I'm Alice!"

Real-world example: Creating a versatile logging system:

function logMessage(level, message) {
    console.log(`[${level}] ${this.timestamp}: ${message}`);
}

const logger = { timestamp: new Date().toISOString() };
logMessage.call(logger, 'INFO', 'User logged in'); 
// "[INFO] 2024-01-15T10:30:00.000Z: User logged in"

The apply() Method

The apply() method is similar to call(), but arguments are passed as an array:

function calculateTotal(tax, discount) {
    const subtotal = this.price * this.quantity;
    const total = subtotal + (subtotal * tax) - discount;
    return total;
}

const order = { price: 25, quantity: 3 };
const total = calculateTotal.apply(order, [0.08, 5]); // 75 + 6 - 5 = 76
console.log(total); // 76

Use case: Finding the maximum value in an array:

const numbers = [23, 45, 67, 89, 12, 34];
const max = Math.max.apply(null, numbers);
console.log(max); // 89

The bind() Method

The bind() method creates a new function with a permanently bound this value:

const customer = {
    name: 'John Doe',
    membership: 'Premium',
    
    getWelcomeMessage() {
        return `Welcome back, ${this.name}! Your ${this.membership} membership is active.`;
    }
};

const boundWelcome = customer.getWelcomeMessage.bind(customer);
setTimeout(boundWelcome, 1000); // Works correctly after 1 second

Real-world example: Creating reusable event handlers:

class NotificationManager {
    constructor() {
        this.notifications = [];
    }
    
    addNotification(message) {
        this.notifications.push({
            message,
            timestamp: new Date(),
            id: Date.now()
        });
        console.log(`Notification added: ${message}`);
    }
    
    setupEventListeners() {
        // Bind maintains the correct 'this' context
        document.getElementById('notify-btn').addEventListener('click', 
            this.addNotification.bind(this, 'Button clicked!')
        );
    }
}

const notificationManager = new NotificationManager();
notificationManager.setupEventListeners();

Arrow Functions and 'this' Binding

Arrow functions have a unique relationship with this - they don't have their own this binding. Instead, they inherit this from the enclosing scope.

How Arrow Functions Handle 'this'

const team = {
    name: 'Development Team',
    members: ['Alice', 'Bob', 'Charlie'],
    
    // Regular function - 'this' refers to the team object
    printMembersRegular() {
        console.log(`${this.name} members:`);
        this.members.forEach(function(member) {
            // 'this' is undefined here (in strict mode)
            console.log(`- ${member} from ${this.name}`); // Error!
        });
    },
    
    // Arrow function solution
    printMembersArrow() {
        console.log(`${this.name} members:`);
        this.members.forEach((member) => {
            // 'this' inherited from printMembersArrow
            console.log(`- ${member} from ${this.name}`); // Works!
        });
    }
};

team.printMembersArrow();
// "Development Team members:"
// "- Alice from Development Team"
// "- Bob from Development Team"
// "- Charlie from Development Team"

When to Use Arrow Functions vs Regular Functions

Use Arrow Functions When:

  • You need to preserve the outer scope's this
  • Writing callbacks and event handlers
  • Working with array methods like map, filter, reduce

Use Regular Functions When:

  • Defining object methods
  • Creating constructors
  • You need dynamic this binding

Frequently Asked Questions

Q: What's the difference between call(), apply(), and bind()?

A: All three methods control the this context, but they work differently:

  • call() immediately invokes the function with individual arguments
  • apply() immediately invokes the function with an array of arguments
  • bind() returns a new function with permanently bound context

Q: Why doesn't 'this' work in arrow functions the same way?

A: Arrow functions don't have their own this binding. They inherit this from the enclosing lexical scope. This makes them perfect for callbacks but unsuitable for object methods that need dynamic this binding.

Q: How do I fix 'this' being undefined in strict mode?

A: In strict mode, this is undefined in functions not called as methods. Solutions include:

  • Using bind() to explicitly set context
  • Using arrow functions to inherit context
  • Calling the function as a method of an object

Q: When should I use bind() vs arrow functions?

A: Use bind() when you need to permanently set context for a regular function. Use arrow functions when you want to inherit the surrounding context, especially in callbacks and event handlers.

Q: Can I change the 'this' binding of an arrow function?

A: No, arrow functions ignore call(), apply(), and bind() attempts to change their this value. They always inherit this from their lexical scope.

Q: What happens to 'this' in nested functions?

A: Each function has its own this binding. In nested regular functions, inner functions don't automatically inherit the outer function's this. Use arrow functions or bind() to preserve context.

Q: How does 'this' work with setTimeout and setInterval?

A: By default, this in setTimeout callbacks refers to the global object (or undefined in strict mode). Use arrow functions or bind() to preserve the intended context.

Q: Is there a performance difference between these binding methods?

A: Arrow functions are generally faster as they don't create new function objects. bind() creates a new function each time it's called, which can impact performance in loops or frequent operations.

Conclusion

Mastering JavaScript's this keyword and binding methods is essential for writing robust, maintainable code. Remember these key points:

  • this is context-dependent and changes based on how functions are called
  • Use call() and apply() for immediate invocation with specific context
  • Use bind() to create permanently bound functions
  • Arrow functions inherit this from their lexical scope
  • Choose the right tool for each situation to write cleaner, more predictable code

Understanding these concepts will make you a more confident JavaScript developer and help you avoid common pitfalls that plague many applications. Practice these examples, experiment with different contexts, and soon you'll have complete mastery over JavaScript's this binding behavior.

Subscribe to our Newsletter

Stay up to date! Get all the latest posts delivered straight to your inbox.

If You Appreciate What We Do Here On TutsCoder, You Should Consider:

If you like what you are reading, please consider buying us a coffee ( or 2 ) as a token of appreciation.

Support Us

We are thankful for your never ending support.

Leave a Comment