Understanding this, bind, call, and apply in JavaScript

In JavaScript, this is a reference to an object. What this refers to can change depending on the context in which a function is called. Sometimes it’s decided implicitly by how the function is called, and sometimes you can set it explicitly using bind, call, and apply.

Even though this might seem confusing at first, it's something you'll run into early when writing JavaScript—whether you're working with the DOM, using object methods, building classes, or writing regular functions.

In this blog, we’ll explore how this behaves in different situations and how you can control it using bind, call, and apply.


Implicit Context

There are four main situations where this is decided based on how the function is called:

  1. In the global context

  2. Inside a method of an object

  3. Inside a constructor function or class

  4. In a DOM event handler


Global Context

When you're in the global context (not inside any object or function), this refers to the global object. In a browser, that's window. In Node.js, it's global.

console.log(this);
// In browser: Window {...}
// In Node.js: global {...}

Functions create their own scope for variables (their own Execution context), but this doesn't follow the same rule. In a top-level function, this still refers to the global object, not the function's local scope.

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

printThis();
// In browser: Window {...}

However, if you use strict mode, this inside a regular function becomes undefined:

'use strict';

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

printThis();
// undefined

Generally, it is safer to use strict mode to reduce the probability of this having an unexpected scope.


As a Method in an Object

When you call a function as a method of an object, this refers to that object:

const country = {
  name: 'USA',
  yearFounded: 1776,
  describe() {
    console.log(`${this.name} was founded in ${this.yearFounded}.`);
  },
};

country.describe();
// "USA was founded in 1776."

In a nested object, this still refers to the immediate object the method belongs to:

const country = {
  details: {
    symbol: 'eagle',
    currency: 'USD',
    printDetails() {
      console.log(`Symbol: ${this.symbol}, Currency: ${this.currency}`);
    },
  },
};

country.details.printDetails();
// "Symbol: eagle, Currency: USD"

this usually refers to the object on the left side of the dot when the function is called.


Constructor Function

When using new to create an object from a constructor function, this refers to the newly created object:

function Country(name, year) {
  this.name = name;
  this.yearFounded = year;

  this.describe = function () {
    console.log(`${this.name} was founded in ${this.yearFounded}.`);
  };
}

const usa = new Country('USA', 1776);
usa.describe();
// "USA was founded in 1776."

Class Constructor

Using the class syntax works the same way for this:

class Country {
  constructor(name, year) {
    this.name = name;
    this.yearFounded = year;
  }

  describe() {
    console.log(`${this.name} was founded in ${this.yearFounded}.`);
  }
}

const usa = new Country('USA', 1776);
usa.describe();
// "USA was founded in 1776."

DOM Event Handler

In an event listener, this usually refers to the element that received the event. In other words, In an event handler, this refers to the element the listener is attached to (same as event.currentTarget). Instead of using this developers often use event.target or event.currentTarget to access the element directly.

const button = document.createElement('button');
button.textContent = 'Click me';
document.body.append(button);

button.addEventListener('click', function () {
  console.log(this); // refers to the button element
});

Output:

<button>Click me</button>

Explicit Context with call, apply, and bind

Sometimes, you need to manually set what this refers to. That’s where call, apply, and bind come in.
All these methods are used for method/functions borrowing/sharing.

call() and apply()

In this example, we’ll create an object, and create a function that references this but has no this context.

const book = {
  title: 'Brave New World',
  author: 'Aldous Huxley',
}

function summary() {
  console.log(`${this.title} was written by ${this.author}.`)
}

summary()

Output:

"undefined was written by undefined"

Since summary and book have no connection, invoking summary by itself will only print undefined, as it’s looking for those properties on the global object.

💡 Note: In strict mode, this is undefined, so trying to access this.title will throw:
Uncaught TypeError: Cannot read property 'title' of undefined.

However, you can use call and apply to invoke the this context of book on the function.

Both call and apply invoke a function immediately and allow you to pass a value for this:

const book = {
  title: 'Brave New World',
  author: 'Aldous Huxley',
};

function summary() {
  console.log(`${this.title} was written by ${this.author}.`);
}

summary.call(book);
summary.apply(book);
// "Brave New World was written by Aldous Huxley."

There is now a connection between book and summary when these methods are applied.

They differ in how arguments are passed:

  • call accepts arguments one-by-one:
function details(genre, year) {
  console.log(`${this.title} is a ${genre} novel written in ${year}.`);
}

details.call(book, 'dystopian', 1932);
// "Brave New World is a dystopian novel written in 1932."
  • apply takes arguments as an array:
details.apply(book, ['dystopian', 1932]);
// Brave New World is a dystopian novel written in 1932

bind()

Unlike call and apply, bind does not execute the function immediately. Instead, it returns a new function with the this value permanently set.

bind can be particularly helpful when you want to use events to access properties of one class within another class. For example, if you were to write a simple game, you might separate the user interface and I/O into one class, and the game logic and state into another. Since the game logic would need to access input, such as key press and click, you would want to bind the events to access the this value of the game logic class.

const getSummary = summary.bind(book);
getSummary(); // "Brave New World was written by Aldous Huxley."

Once a function is bound, you can't rebind it:

const book2 = {
  title: '1984',
  author: 'George Orwell',
};

getSummary.bind(book2);
getSummary(); // Still prints for Brave New World

Arrow Functions and this

Arrow functions behave differently—they don’t have their own this. Instead, they inherit this from their surrounding context.

const person = {
  name: 'Leslie',
  regularFunc: function () {
    console.log(this.name);
  },
  arrowFunc: () => {
    console.log(this.name);
  },
};

person.regularFunc(); // 'Leslie'
person.arrowFunc(); // undefined (inherits from outer scope)
  • arrowFunc is defined inside the object literal, not inside a method or class.

  • So its this comes from the outer (global) context — not from the person object.

  • In the global scope (in browsers), this is usually window, and window.name is usually "" or undefined unless explicitly set.

This is especially useful when dealing with callbacks and event listeners in classes:

const button = document.createElement('button');
button.textContent = 'Click me';
document.body.append(button);

class Display {
  constructor() {
    this.text = 'Updated text';
    button.addEventListener('click', event => {
      event.target.textContent = this.text;
    });
  }
}

new Display();

If you click the button, the text content will change to the value of buttonText. If you hadn’t used an arrow function here, this would be equal to event.currentTarget (as seen in DOM event handler), and you wouldn’t be able to use it to access a value within the class without explicitly binding it.


Key Differences: call, apply, and bind

MethodExecutes Function Immediately?Argument FormatReturnsUse Case
callYesComma-separated valuesFunction resultCall a function with a specific this and arguments
applyYesArguments as an arrayFunction resultSame as call, useful when arguments are in an array
bindNoComma-separated valuesNew functionCreate a new function with this permanently bound

When to Use What

  • Use call when you want to invoke a function immediately with a custom this and a list of arguments.

      func.call(obj, arg1, arg2);
    
  • Use apply when arguments are already in an array and you want to call the function immediately.

      func.apply(obj, [arg1, arg2]);
    
  • Use bind when you want to create a new function with this bound to a specific object but invoke it later.

      const newFunc = func.bind(obj);
      newFunc(arg1, arg2);
    

Thanks for reading! I hope this post helped you understand how this, call, apply, and bind work in JavaScript. If you found it helpful, feel free to share it with others or drop your thoughts in the comments. Happy coding! 🚀

0
Subscribe to my newsletter

Read articles from Deepthi Purijala directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Deepthi Purijala
Deepthi Purijala

Full Stack Developer with hands-on experience of more than 1 year. Proficient in both Back-end and Front-end technologies, with a strong commitment to delivering high-quality code