[Advanced JS] Call, Apply, Bind in JavaScript: Clear Examples and Practical Applications

In the world of JavaScript, the this keyword can be both a powerful ally and a tricky foe. How this is determined in different contexts is one of the most confusing concepts for developers. Luckily, JavaScript gives us a trio of "superheroes" to tame this: call(), apply(), and bind().

Call, Apply, Bind in JavaScript

Mastering these three methods not only helps you solve complex this issues, but also opens up more flexible, efficient, and reusable ways to write code. Let’s dive deeper together! 🚀

The Problem: When "this" Goes Rogue

Before we get into details, let’s look at a classic example to see why we need call, apply, and bind.

const person = {
  name: 'John Doe',
  greet: function () {
    console.log(`Hello, I am ${this.name}`)
  },
}

const greetFunc = person.greet

greetFunc() // Result: "Hello, I am undefined"

Why undefined? Because when we assign person.greet to greetFunc and call it independently, this inside greetFunc no longer refers to the person object. Instead, it points to the global object (window in browsers) in non-strict mode, or is undefined in strict mode. This is where call, apply, and bind shine.

Call: Borrow a Method Directly

The call() method lets you invoke a function and explicitly set the value of this inside that function. You can also pass arguments individually, separated by commas.

Syntax

function.call(thisArg, arg1, arg2, ...);
  • thisArg: The value you want this to be inside the function.
  • arg1, arg2, ...: Arguments passed to the function.

Practical Example

Let’s fix the original problem using call():

const person = {
  name: 'John Doe',
  greet: function () {
    console.log(`Hello, I am ${this.name}`)
  },
}

const anotherPerson = {
  name: 'Jane Smith',
}

person.greet.call(anotherPerson) // Result: "Hello, I am Jane Smith"

Here, we "borrowed" the greet method from person and executed it in the context of anotherPerson. this inside greet is now set to anotherPerson, so this.name returns "Jane Smith".

Apply: Call’s Twin Brother

apply() works almost exactly like call(): it also executes a function with a specified this value. The only difference is how arguments are passed. Instead of passing arguments individually, apply() takes an array (or array-like object) of arguments.

Syntax

function.apply(thisArg, [argsArray]);
  • thisArg: Same as call().
  • [argsArray]: An array or array-like object containing arguments for the function.

Practical Example

Let’s look at a function that needs multiple arguments:

function introduce(profession, city) {
  console.log(`I am ${this.name}, a ${profession} from ${city}.`)
}

const person = {
  name: 'Peter Pan',
}

const details = ['software engineer', 'Neverland']

introduce.apply(person, details)
// Result: "I am Peter Pan, a software engineer from Neverland."

apply() is especially useful when you have an array of data and want to pass its elements as arguments to a function.

Bind: Create a Permanently "Bound" Copy

Unlike call() and apply(), which execute the function immediately, bind() does not call the function. Instead, it creates a new function with this permanently bound to the value you specify.

Syntax

function.bind(thisArg, arg1, arg2, ...);
  • thisArg: The value that this will always refer to in the new function.
  • arg1, arg2, ...: (Optional) Arguments to be partially applied.

Practical Example

bind() is the perfect solution for our original problem, especially in situations like event handling or callbacks.

const person = {
  name: 'Alice Wonderland',
  greet: function () {
    console.log(`Hello, I am ${this.name}`)
  },
}

const boundGreet = person.greet.bind(person)

boundGreet() // Result: "Hello, I am Alice Wonderland"

// Even in a callback, `this` stays correct
setTimeout(boundGreet, 1000) // After 1 second: "Hello, I am Alice Wonderland"

The boundGreet function is a new version of person.greet where this will always be person, no matter how or where it’s called.

Quick Comparison: Call, Apply, and Bind

A quick summary table of Call, Apply, and Bind by key criteria:

Criteriacall()apply()bind()
ExecutionCalls function immediatelyCalls function immediatelyDoes not call, returns new function ❌
ArgumentsIndividually (arg1, arg2, ...)As an array ([arg1, arg2])Individually, can be partially applied
Return valueReturn value of original functionReturn value of original functionNew function with bound this

Memory tip:

  • Call: Comma-separated arguments.
  • Apply: Array of arguments.
  • Bind: Bound function.

Conclusion: When to Use Which?

Common real-world scenarios for using call(), apply(), and bind() effectively:

  • Use call() or apply() when you want to execute a function immediately with a different this context.

    • Method borrowing: Borrow methods from other objects, e.g., use Array.prototype.slice.call(arguments) to convert arguments to a real array.
    • Calling functions with dynamic argument arrays: Use apply() to pass an array of values to functions like Math.max or Math.min. Example: Math.max.apply(null, [1, 5, 2, 9]).
  • Use bind() when you need a function to use later, but want to ensure its this context never changes.

    • Event listeners and callbacks: The most common case. When passing an object’s method as an event handler (e.g., element.addEventListener('click', myObject.myMethod)), this is lost. bind() is the perfect fix: element.addEventListener('click', myObject.myMethod.bind(myObject)).
    • Partial application: bind() lets you create new functions with some arguments preset.
    function multiply(a, b) {
      return a * b
    }
    
    const double = multiply.bind(null, 2) // `this` is irrelevant, set to null
    console.log(double(5)) // Result: 10 (same as multiply(2, 5))
    

In short, call(), apply(), and bind() are powerful tools in every JavaScript developer’s toolkit. They not only give you full control over the this keyword, but also encourage modular and reusable code. By understanding their subtle differences and ideal use cases, you can write cleaner, more predictable, and less error-prone code.

Next time you face a "lost" this, remember this powerful trio!

Related Posts

[JS Basics] Conditional Statements in JavaScript: Examples & Effective Usage

Master common conditional statements in JavaScript like if, if-else, switch. Detailed guide from syntax to practical application in real-world projects.

[JS Basics] Loops in JavaScript: A Detailed Guide for Beginners

'JavaScript loops are essential tools. Discover how to use for, while, and other loops effectively to optimize your code, with practical examples and expert tips.

[Advanced JS] Data Structures & Algorithms: Practical Applications with JavaScript

Apply Data Structures and Algorithms to optimize web app performance. Level up your clean and efficient coding skills with JavaScript in this in-depth article.

[JS Basics] Objects in JavaScript: Concepts & Effective Usage

A deep dive into how Objects work in JavaScript. Detailed guide on properties, methods, and how to use Objects to write clean and efficient code.