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

*By Saurabh Prajapati | Full-Stack Engineer at IBM India Software Lab*

* * *

## Why I Decided to Explore This

I'll be honest — for the longest time, `this` in JavaScript confused the heck out of me.

I'd write some code, it would work. I'd move the same function somewhere else, and suddenly `this` was `undefined`. I'd scratch my head, Google it, patch it with an arrow function, and move on without really understanding *why*.

Sound familiar?

After seeing `call()`, `apply()`, and `bind()` pop up again and again — in interview questions, open-source code, and MDN docs — I decided: *okay, let's actually figure this out.*

This blog is me documenting everything that finally made it click. Let's go.

* * *

## 1\. What Is `this` in JavaScript?

Here's the simplest way to think about it:

> `this` **= "who is calling the function right now?"**

`this` is not about *where* the function is written. It's about *who is calling it* at the moment it runs.

Think of it like a name tag that changes depending on who's wearing it.

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

If `this` is the `window` object, it looks for `window.name`.  
If `this` is a `user` object, it looks for `user.name`.

Same function. Different `this`. Different result. That's the core idea.

* * *

## 2\. `this` Inside Normal Functions

Let's start simple.

```js
function sayHello() {
  console.log(this);
}

sayHello();
```

In a browser, this logs the `window` **object** (in non-strict mode).  
In strict mode (`"use strict"`), `this` is `undefined`.

This tripped me up early on. I expected `this` to mean "the function itself." Nope. When no one specifically "calls" the function as a method, `this` defaults to the global object — or `undefined` in strict mode.

> **I wish I knew this earlier:** `this` inside a plain function call doesn't refer to the function. It refers to whatever called it — often `window` by default.

* * *

## 3\. `this` Inside Objects

Now it gets more interesting.

```js
const developer = {
  name: "Saurabh",
  greet: function () {
    console.log("Hi, I'm " + this.name);
  },
};

developer.greet(); // Hi, I'm Saurabh
```

Here, `developer` is calling `greet()`. So `this` = `developer`. Makes sense, right?

But here's where it gets weird:

```js
const greetFn = developer.greet;
greetFn(); // Hi, I'm undefined
```

Wait, what?

When you *detach* the function from the object and call it on its own, `this` no longer points to `developer`. It goes back to the global object (or `undefined` in strict mode).

This is the exact moment I thought — *"Okay JavaScript, you're doing this on purpose to mess with me."*

But it actually makes sense once you remember the rule: `this` **depends on WHO is calling the function, not where the function lives.**

* * *

## 4\. What Does `call()` Do?

`call()` lets you **manually set** `this` when calling a function.

```js
function greet(role) {
  console.log(`Hi, I'm ${this.name} and I work as a ${role}`);
}

const person = { name: "Saurabh" };

greet.call(person, "Software Engineer");
// Hi, I'm Saurabh and I work as a Software Engineer
```

Think of `call()` like saying:  
*"Hey function, run right now — and treat THIS object as your* `this`*."*

### Syntax

```js
functionName.call(thisArg, arg1, arg2, ...);
```

*   `thisArg` → the object you want `this` to be
    
*   `arg1, arg2, ...` → normal function arguments, passed one by one
    

### Real Use Case — Method Borrowing

```js
const ibmDev = {
  name: "Saurabh",
  company: "IBM",
};

const visileanDev = {
  name: "Rahul",
  company: "Visilean",
};

function introduce() {
  console.log(`I'm ${this.name} from ${this.company}`);
}

introduce.call(ibmDev);     // I'm Saurabh from IBM
introduce.call(visileanDev); // I'm Rahul from Visilean
```

One function. Two different objects. `call()` makes it work for both.  
This is called **function borrowing** — and it's actually super useful.

* * *

## 5\. What Does `apply()` Do?

`apply()` is almost identical to `call()`.

The **only difference**: instead of passing arguments one by one, you pass them as an **array**.

```js
function greet(role, city) {
  console.log(`Hi, I'm ${this.name}, a ${role} from ${city}`);
}

const person = { name: "Saurabh" };

// call() — arguments one by one
greet.call(person, "Software Engineer", "India");

// apply() — arguments as an array
greet.apply(person, ["Software Engineer", "India"]);
```

Both produce the exact same output:

```plaintext
Hi, I'm Saurabh, a Software Engineer from India
```

### When is `apply()` Useful?

It shines when your arguments are already in an array — like when using `Math.max`:

```js
const scores = [85, 92, 78, 96, 88];

console.log(Math.max.apply(null, scores)); // 96
```

We pass `null` as `thisArg` because `Math.max` doesn't use `this`. We just need the array-spread behavior.

> **Honest take:** With modern JavaScript, you can use the spread operator (`...`) instead of `apply()` in many cases. But knowing `apply()` helps you read older codebases — and it comes up in interviews *all the time*.

* * *

## 6\. What Does `bind()` Do?

Here's where things get really interesting.

`bind()` doesn't call the function immediately. Instead, it **returns a new function** with `this` permanently bound to an object.

```js
function greet() {
  console.log(`Hi, I'm ${this.name}`);
}

const person = { name: "Saurabh" };

const boundGreet = greet.bind(person);

// Call it whenever you want
boundGreet(); // Hi, I'm Saurabh
boundGreet(); // Hi, I'm Saurabh (still Saurabh, always Saurabh)
```

You get a function that remembers its `this`. Forever.

### Real Use Case — Event Listeners

This is where `bind()` saves the day in React and vanilla JS:

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

// Without bind — 'this' would be the DOM element, not the button object
document.querySelector("button").addEventListener(
  "click",
  button.handleClick.bind(button)
);
```

I used this pattern a lot while building components at IBM. When you pass a method as a callback, it loses its `this`. `bind()` fixes that cleanly.

### Pre-set Arguments with `bind()`

Here's a bonus feature I discovered — you can also pre-fill arguments:

```js
function multiply(a, b) {
  return a * b;
}

const double = multiply.bind(null, 2);

console.log(double(5));  // 10
console.log(double(9));  // 18
```

`2` is always the first argument. You just supply the second. This pattern is called **partial application** — and it feels like magic once you get it.

* * *

## 7\. The Difference — `call` vs `apply` vs `bind`

Here's the table I wish someone had shown me from the beginning:

| Feature | `call()` | `apply()` | `bind()` |
| --- | --- | --- | --- |
| Calls the function immediately? | ✅ Yes | ✅ Yes | ❌ No |
| Returns a new function? | ❌ No | ❌ No | ✅ Yes |
| How to pass arguments | One by one | As an array | One by one (pre-set) |
| Use when | You want to run now with a custom `this` | Args are in an array | You want to save for later |

**Simple memory trick I use:**

*   `call` → *C*all it now, *C*omma-separated args
    
*   `apply` → *A*pply it now, *A*rray of args
    
*   `bind` → *B*ind it for later, returns a *B*ound function
    

* * *

## 8\. Visualizing the Relationship

Here's how I think about it mentally:

```plaintext
Regular Function Call
─────────────────────
sayHello()
    └── this = window (or undefined in strict mode)


Object Method Call
──────────────────
developer.greet()
    └── this = developer ✅


call() / apply()
─────────────────
greet.call(customObj, args)
    └── this = customObj ✅ (runs immediately)


bind()
──────
const fn = greet.bind(customObj)
fn()
    └── this = customObj ✅ (runs later, always bound)
```

* * *

## 9\. Hands-On Assignment — Try This Yourself!

Here's a small exercise I'd encourage you to do right now. Open your browser console or a CodeSandbox and follow along.

### Step 1 — Create an Object with a Method

```js
const developer = {
  name: "Saurabh",
  skills: ["React", "Node.js", "GenAI"],
  introduce: function (role, company) {
    console.log(
      `Hi! I'm ${this.name}. I work as a ${role} at ${company}.`
    );
    console.log(`My skills: ${this.skills.join(", ")}`);
  },
};

developer.introduce("Software Engineer", "IBM");
```

### Step 2 — Borrow the Method with `call()`

```js
const anotherDev = {
  name: "Priya",
  skills: ["Vue.js", "Python", "Docker"],
};

developer.introduce.call(anotherDev, "Backend Engineer", "Infosys");
```

### Step 3 — Use `apply()` with Array Arguments

```js
const args = ["Full-Stack Engineer", "Google"];

developer.introduce.apply(anotherDev, args);
```

### Step 4 — Use `bind()` and Store the Function

```js
const boundIntroduce = developer.introduce.bind(anotherDev);

// Use it anywhere, anytime
boundIntroduce("Frontend Engineer", "Flipkart");

// Still works!
setTimeout(boundIntroduce, 1000, "DevOps Engineer", "Microsoft");
```

Try changing the objects. Try passing different arguments. Break it and fix it — that's how it really sticks.

* * *

## Key Discoveries from My Journey

A few things that genuinely surprised me:

*   **Arrow functions don't have their own** `this`**.** They inherit `this` from the surrounding scope. So `call()`, `apply()`, and `bind()` have no effect on them. Took me way too long to figure this out.
    
*   `bind()` **is a lifesaver in React class components.** That whole `this.handleClick = this.handleClick.bind(this)` in the constructor? Now I actually understand why it's there.
    
*   `apply()` **was more useful before the spread operator.** Now `Math.max(...scores)` does the same thing as `Math.max.apply(null, scores)`. But `apply()` is still worth knowing.
    
*   `this` **is runtime, not write-time.** It's determined when the function is *called*, not when it's *defined*. That single realization fixed 80% of my `this` confusion.
    

* * *

**How would you approach this? Do you have a better mental model for** `this`**?**  
Drop a comment or connect with me — I'd love to hear how you think about it!

* * *

## About the Author

**Saurabh Prajapati** is a Full-Stack Software Engineer at IBM India Software Lab, where he builds cloud-native enterprise solutions for Maximo. He specializes in GenAI, React, and modern web technologies — and loves turning confusing concepts into approachable learning experiences.

*   🐙 GitHub: [prajapatisaurabh](https://github.com/prajapatisaurabh)
    
*   💼 LinkedIn: [saurabh-prajapati](https://linkedin.com/in/saurabh-prajapati)
    
*   📧 Email: saurabhprajapati120@gmail.com
