Skip to main content

Command Palette

Search for a command to run...

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

Published
5 min read
The Magic of this, call(), apply(), and bind() in JavaScript
S
Full-stack developer obsessed with performance, scalability, and clean systems. I use Arch btw.

Introduction

If you've spent any time with JavaScript, you've probably run into this and walked away confused. It behaves differently depending on where it shows up. And call(), apply(), and bind() sound similar enough that most people lump them together without really understanding what each one does differently.

This post clears all of that up. No execution context internals, no spec language. Just what you need to know to write better code.


What this Actually Means

The simplest way to think about this: it refers to whoever called the function.

Not where the function was defined. Not where it lives in your code. Who called it.

That one shift in thinking makes most of the confusion go away.


this Inside a Regular Function

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

greet();

When you call a plain function like this, this points to the global object. In a browser, that's window. In Node.js, it's global. In strict mode, it's undefined.

Nobody specific called it, so JavaScript falls back to the global object by default.


this Inside an Object

Things get more useful when functions live inside objects.

const person = {
    name: "Alice",
    greet: function () {
        console.log("Hi, I'm " + this.name);
    }
};

person.greet(); // Hi, I'm Alice

Here, person is the one calling greet. So this is person, and this.name is "Alice".

Now watch what happens when you pull the function out of the object:

const greet = person.greet;
greet(); // Hi, I'm undefined

The function is the same. But now nobody owns the call. this falls back to the global object, and name isn't defined there, so you get undefined.

This is the source of most this bugs.


call(): Borrow a Function, Choose Your this

call() lets you run a function and tell it exactly what this should be.

const person1 = { name: "Alice" };
const person2 = { name: "Bob" };

function greet(city) {
    console.log(this.name + " lives in " + city);
}

greet.call(person1, "New York"); // Alice lives in New York
greet.call(person2, "London");   // Bob lives in London

The first argument to call() sets this. Everything after that gets passed as individual arguments to the function.

Think of it as: "Run this function, but pretend it belongs to this object."


apply(): Same Idea, Arguments as an Array

apply() works exactly like call(). The only difference is how you pass arguments. Instead of listing them one by one, you pass an array.

function greet(city, country) {
    console.log(this.name + " is from " + city + ", " + country);
}

const person = { name: "Alice" };

greet.apply(person, ["Paris", "France"]); // Alice is from Paris, France

Both lines below do the same thing:

greet.call(person, "Paris", "France");
greet.apply(person, ["Paris", "France"]);

apply() is handy when your arguments are already stored in an array.


bind(): Create a Fixed Version of a Function

call() and apply() run the function immediately. bind() is different. It returns a new function with this permanently locked in, but doesn't call it yet.

const person = { name: "Alice" };

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

const greetAlice = greet.bind(person);

greetAlice(); // Hi, I'm Alice
greetAlice(); // Hi, I'm Alice (always Alice, no matter what)

You can store that bound function and call it later. It will always use the this you gave it.

This is especially useful for event handlers and callbacks, where you lose control of what this will be.

const button = {
    label: "Submit",
    handleClick: function () {
        console.log("Clicked: " + this.label);
    }
};

const handler = button.handleClick.bind(button);

// Later, passed as a callback
setTimeout(handler, 1000); // Clicked: Submit

Without bind(), this inside setTimeout would not be button.


Comparison: call vs apply vs bind

call() apply() bind()
Runs the function? Yes, immediately Yes, immediately No, returns a new function
How you pass args One by one As an array One by one (at bind time or call time)
Fixes this? For that call only For that call only Permanently
Returns Function result Function result New function

When to Use Each One

Use call() when you want to run a function once with a specific this and you have the arguments ready individually.

Use apply() when you have the same situation but your arguments are already in an array.

Use bind() when you need to pass a function somewhere (a callback, an event listener, a timeout) and you want to make sure this stays what you intend it to be.


One More Thing Worth Knowing

Arrow functions ignore all of this. They don't have their own this. They inherit it from the surrounding scope and call(), apply(), and bind() cannot change that.

const person = {
    name: "Alice",
    greet: () => {
        console.log(this.name); // undefined, always
    }
};

person.greet();

Arrow functions are great in many situations, but if you need this to refer to the object, use a regular function.


Conclusion

Once you stop thinking of this as something mysterious and start thinking of it as "who called this function," the rest follows. call() and apply() let you pick the caller. bind() locks one in for good.