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

If you have spent any time learning JavaScript, you have likely encountered the this keyword. You have also likely felt completely betrayed by it when it didn't do what you expected.

Don't worry—you are not alone. this is one of the most notoriously confusing concepts in JavaScript. But the confusion usually disappears once you understand one fundamental rule: this is not about where a regular function is written; it is entirely about who is calling the function.

Let's break down exactly what this means, how it behaves in different scenarios, and how you can take total control of it using call(), apply(), bind(), and modern Arrow Functions.

1. What exactly is this? (The Simple Explanation)

In the English language, we use pronouns like "he," "she," or "it" so we don't have to constantly repeat someone's name. In JavaScript, this acts like a pronoun for objects. It is a shortcut reference to the "current" object that is executing the code.

The golden rule to remember is: Look to the left of the dot. When a function is invoked, whatever object is standing immediately to the left of the dot is the object that this points to. If there is no dot, the default rules apply.

2. this Inside Normal Functions

What happens if you just call a regular, standalone function?

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

sayHello(); 

Notice how we called sayHello() without any object attached to it. Because there is no specific "owner" calling the function, JavaScript defaults to the global environment.

  • In a web browser, this will point to the Global window object.

  • (Note: If you are writing modern JavaScript using "strict mode", JavaScript refuses to guess, and this will simply be undefined.)

3. this Inside Objects

When a function is stored inside an object, we call it a method. This is where this behaves most predictably. When an object calls its own method, this points directly to that object.

const user = {
  username: "CodeNinja",
  role: "Admin",
  printProfile: function() {
    // "this" points to the "user" object
    console.log(`User: \({this.username}, Role: \){this.role}`);
  }
};

user.printProfile(); 
// Output: User: CodeNinja, Role: Admin

Because user is on the left side of the dot (user.printProfile()), user is the one "calling" the function. Therefore, this equals user.

4. The Plot Twist: Arrow Functions (=>)

Here is where modern JavaScript changes the game. Arrow functions completely ignore the "who called me?" rule.

Arrow functions do not have their own this. Instead, they borrow (or inherit) this from the surrounding code where the function was originally written. In programming terms, this is called lexical scoping. They look up to the nearest parent function that isn't an arrow function and use its this.

This is incredibly useful for things like timers or array methods where a regular function would accidentally lose its this reference:

const team = {
  teamName: "The Avengers",
  members: ["Iron Man", "Thor"],
  showRoster: function() {
    // The parent method 'showRoster' is called by 'team', so 'this' = team

    this.members.forEach((member) => {
      // The arrow function doesn't have its own 'this'. 
      // It borrows 'this' from showRoster!
      console.log(`\({member} is on \){this.teamName}`);
    });
  }
};

team.showRoster();
// Output: 
// Iron Man is on The Avengers
// Thor is on The Avengers

(If we had used a regular function(member) inside that forEach, it would have forgotten the team object and this.teamName would be undefined!)

5. Taking Control: call(), apply(), and bind()

Sometimes, the default rules get in our way. What if we want an object to use a method that belongs to a different object? We can "borrow" functions and force this to point exactly where we want it to.

JavaScript provides three built-in methods for this: call(), apply(), and bind().

call() – The Direct Borrower

The call() method allows you to execute a function immediately, but you get to dictate exactly what this should be. You pass the target object as the first argument, followed by any normal arguments the function needs, separated by commas.

const dog = { species: "Canine" };
const cat = { species: "Feline" };

function describeAnimal(name, age) {
  console.log(`\({name} is a \){age}-year-old ${this.species}.`);
}

// We borrow the function and force 'this' to point to the 'dog' object
describeAnimal.call(dog, "Buddy", 3); 
// Output: Buddy is a 3-year-old Canine.

// Now we do it for the cat
describeAnimal.call(cat, "Luna", 2); 
// Output: Luna is a 2-year-old Feline.

apply() – The Array Specialist

apply() works exactly the same way as call(). It executes the function immediately and changes the this context.

The only difference is how it handles additional arguments. Instead of passing them one by one separated by commas, apply() takes them as a single array.

const account = { balance: 500 };

function processTransaction(type, amount) {
  console.log(`${type} of $${amount} on account with $${this.balance}.`);
}

// Arguments are bundled into an array: ["Withdrawal", 50]
processTransaction.apply(account, ["Withdrawal", 50]);
// Output: Withdrawal of \(50 on account with \)500.

Pro-tip: apply() is incredibly useful when you already have your data stored in an array and need to pass it to a function that expects separate arguments.

bind() – The Future Planner

While call() and apply() execute the function right away, bind() does not.

Instead, bind() returns a brand new function with this permanently locked to the object you provided. You can then store this new function in a variable and run it later.

const server = { port: 8080 };

function connect(protocol) {
  console.log(`Connecting to \({protocol} on port \){this.port}...`);
}

// We don't want to connect yet. We just want to prep the function.
const connectToMyServer = connect.bind(server, "HTTPS");

// Much later in the application...
connectToMyServer(); 
// Output: Connecting to HTTPS on port 8080...

bind() is essential in JavaScript, especially when passing methods as callbacks (like in React or event listeners) where the function might otherwise "lose" its original this context.

6. Your Turn: The this Assignment

Reading about code is great, but writing it is how you learn. Open your browser's developer console (F12) and try to complete this assignment:

  1. Create the Base Object: Create an object called robot with a property model (e.g., "T-800") and a method called diagnostics that logs "Running diagnostics on " + this.model.

  2. Borrow with call(): Create a second object called drone with a model property. Use call() to make the robot's diagnostic method run, but point this to the drone.

  3. Use apply(): Write a standalone function called upgrade that takes two arguments: part1 and part2. It should log the this.model getting those parts. Use apply() to run this function on your robot, passing an array of parts.

  4. Use bind(): Use bind() to create a permanently locked version of the diagnostics method that is forever bound to the drone. Store it in a variable called checkDrone and execute it.

  5. The Arrow Test: Inside the robot object, add a property called tasks (an array of strings). Create a method called listTasks that uses .forEach() and an arrow function to log this.model along with each task.

JS Under the Hood: The Engine Room

Part 12 of 24

Ever wondered why your code actually runs? We’re going beyond the syntax to explore the V8 engine, memory management, and the "magic" that happens between your keyboard and the screen. Just deep dives.

Up next

JavaScript Modules: Import and Export Explained

function add(a, b) { return a + b; } function multiply(a, b) { return a * b; } function log(message) { console.log(message); } const sum = add(2, 3); const product = multiply(2, 3); log(`Sum