Skip to main content

Command Palette

Search for a command to run...

The Magic of this, call(), apply(), and bind() in JavaScript

Updated
6 min read
The Magic of this, call(), apply(), and bind() in JavaScript

🤔 What is this?

Here's the simplest way to think about it:

this is whatever object is calling the function.

That's it. this is not a fixed value. It changes depending on who calls the function.

Think of it like asking "Who sent this message?" The answer changes depending on who actually sent it.


🔍 this Inside a Normal Function

When you call a function on its own (not inside any object), this refers to the global object — which is window in a browser.

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

showThis(); // window (in browser)

In strict mode, this inside a standalone function is undefined:

"use strict";

function showThis() {
  console.log(this); // undefined
}

showThis();

Don't worry too much about window and strict mode for now. Just remember: a standalone function has no clear "owner", so this defaults to global or undefined.


🏠 this Inside an Object

This is where this becomes super useful. When a function lives inside an object (called a method), this refers to that object.

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

person.greet(); // Hello, I am Arjun

Why does this.name give "Arjun"? Because person is the one calling greet(). The object before the dot is always this.

// The object before the dot ↓ is `this`
person.greet();
// ^^^^^^ this = person

Four practical rules to determine this

  1. Default binding: standalone function → global object (or undefined in strict mode).

  2. Implicit binding: obj.method() → this is the object before the dot.

  3. Explicit binding: call/apply/bindthis is what you pass.

  4. new binding: when called with new, this is the newly created instance.


🪄 Now the Magic: Borrowing Functions

Here's a question. What if you have another object that doesn't have a greet method, but you want to use person's greet for it?

That's exactly what call(), apply(), and bind() are for — they let you borrow a function and tell it which object to use as this.


📞 call(), apply(), and bind() — borrow a function and set this

These three methods let you explicitly choose what this should be when a function runs.

  • call(thisArg, arg1, arg2, ...) — invoke the function immediately with this set to thisArg and arguments listed individually.

  • apply(thisArg, [argsArray]) — invoke immediately with this set to thisArg and arguments provided as an array.

  • bind(thisArg, ...presetArgs) — return a new function permanently bound to thisArg (and optionally preset arguments); it does not call the function immediately.

Examples

function greet(greeting, punctuation) {
  console.log(greeting + ', ' + this.name + punctuation);
}

const person = { name: 'Arjun' };
const stranger = { name: 'Sam' };

// call: args listed
greet.call(person, 'Hi', '!');    // Hi, Arjun!

// apply: args as array
greet.apply(stranger, ['Hello', '...']); // Hello, Sam...

// bind: returns a new function bound to `person`
const greetArjun = greet.bind(person, 'Hey');
greetArjun('?'); // Hey, Arjun?

When to use which

  • Use call when you want to call a function right now and have arguments handy as separate values.

  • Use apply when your arguments are already in an array (common when forwarding arguments).

  • Use bind when you need a stable function reference that keeps the intended this for later use (callbacks, event listeners, timers).

Common scenario: method extracted and losing this

const user = {
  name: 'Lina',
  sayName() {
    console.log(this.name);
  }
};

const fn = user.sayName;
fn(); // undefined (or global name) — lost implicit binding

const boundFn = user.sayName.bind(user);
boundFn(); // Lina — `this` restored

Arrow functions and this

  • Arrow functions do not have their own this. They inherit this lexically from the surrounding scope.

  • This makes arrow functions useful when you want to preserve the outer this without using bind.

Example: arrow function avoids bind inside a method

const obj = {
  name: 'Maya',
  delayedGreet() {
    setTimeout(() => {
      // arrow function uses `this` from delayedGreet's scope
      console.log('Hello, ' + this.name);
    }, 100);
  }
};

obj.delayedGreet(); // Hello, Maya

📊 call vs apply vs bind — At a Glance

[call vs apply vs bind Comparison Table]

call() apply() bind()
Calls immediately? ✅ Yes ✅ Yes ❌ No
Arguments passed as Comma separated Array [ ] Comma separated
Returns Result of the call Result of the call A new function
Use when Borrow once Args in an array Save & call later
Example fn.call(obj, a, b) fn.apply(obj, [a,b]) fn.bind(obj)

✍️ Practice Assignment

Try this step by step in your browser console (F12 → Console).

Task 1 — Create an object with this

const doctor = {
  name: "Dr. Mehta",
  introduce: function(speciality, hospital) {
    console.log(`I am \({this.name}, a \){speciality} at ${hospital}.`);
  }
};

// Direct call — this = doctor
doctor.introduce("Cardiologist", "Apollo"); 
// I am Dr. Mehta, a Cardiologist at Apollo.

Task 2 — Borrow using call()

const nurse = { name: "Anjali" };

// Borrow doctor's introduce for nurse
doctor.introduce.call(nurse, "Nurse", "Lilavati");
// I am Anjali, a Nurse at Lilavati.

Task 3 — Use apply() with an array

const args = ["Surgeon", "Kokilaben"];

doctor.introduce.apply(nurse, args);
// I am Anjali, a Surgeon at Kokilaben.

Task 4 — Use bind() to save for later

const patient = { name: "Ramesh" };

const patientIntro = doctor.introduce.bind(patient);

// Call later — this is permanently patient
patientIntro("Patient", "City Hospital");
// I am Ramesh, a Patient at City Hospital.

🎯 Quick Recap

  • this = the object that is currently calling the function

  • The object before the dot is always this

  • call() — borrows a function and runs it now, args comma-separated

  • apply() — same as call(), but args passed as an array

  • bind() — returns a new function with this locked in, doesn't run immediately

  • All three let you manually control what this means inside a function