Quick Access

Favorites

No favorites yet

Star your frequently used calculators to access them quickly

Browse Calculators
Saved Color Pairs
JavaScriptJavaScript

Simplifying JavaScript's call(), apply(), and bind(): Analogies, Examples, and Use Cases

Avatar for Subhro Kar
Subhro Kar
Published on February 8, 2025

The Problem: The Mystery of this

In JavaScript, the value of this depends on how a function is called. Sometimes, you want to control what this refers to. This is where call, apply, and bind come in. Think of them as tools to "steer" the this keyword explicitly.


The Analogy: A Car (Function) and Its Driver (this)

Imagine functions as cars. Normally, the driver (this) is determined by who starts the car (calls the function). But what if you want to loan your car to a friend (a different object)? call, apply, and bind let you do this:

  • call: "Drive my car now, and here’s a list of instructions (arguments)."
  • apply: "Drive my car now, and here’s a box of instructions (array)."
  • bind: "Here’s a clone of my car pre-configured for your use. Drive it whenever you want."

Interactive Examples

1. call(): Immediate Execution with Arguments

call() invokes a function immediately, specifying this and passing arguments individually.

Without call, apply, or bind, the this context will be either:

  • undefined (in strict mode) or
  • The global object (in non-strict mode)
1// Define objects and function 2const person1 = { name: "Alice" }; 3const person2 = { name: "Bob" }; 4 5function greet(greeting, punctuation) { 6 console.log("this value:", this); 7 console.log(`${greeting}, ${this.name}${punctuation}`); 8} 9// Call without setting 'this' context 10console.log("Calling without call/apply/bind:"); 11greet("Hi", "!"); // Will throw error or show undefined for this.name 12

Try it yourself! Modify the code below:

index.js

Your Turn:

  1. Add a person3 with a new name.
  2. Call greet with person3 and a custom greeting.

2. apply(): Immediate Execution with an Array

apply() works like call(), but accepts arguments as an array.

index.js

Your Challenge:

  1. Create an array newArgs with different values.
    • First element: A different color (e.g., "black", "silver", "white")
    • Second element: A year between 2020-2024
    • const newArgs = ["silver", 2022]; // Try changing these values
  2. Use apply() to run describeCar with newArgs. describeCar.apply(car, newArgs);

3. bind(): Create a Reusable Function with Preset this

bind() returns a new function with a fixed this value. You can call it later!

index.js

Your Task:

  1. Use bind() to create a function where this is { name: "YourPet" }.
  2. Call it with a custom sound.

Deep Dive: How Each Method Works


call(): Borrow a Function Immediately

The call() method in JavaScript is used to invoke a function immediately with a specified this value and individual arguments. Here's a detailed explanation:

  1. Purpose: The primary purpose of call() is to execute a function with a specific this context. This is particularly useful when you want to borrow a method from one object and use it on another object.
  2. Syntax: The syntax for using call() is func.call(thisArg, arg1, arg2, ...). Here, func is the function you want to invoke, thisArg is the value you want to use as this inside the function, and arg1, arg2, ... are the individual arguments you want to pass to the function.
  3. How it Works: When you use call(), the function is executed immediately. The this value inside the function is set to the thisArg you provide. The remaining arguments are passed to the function as individual parameters.

Example:

1const person1 = { name: "Alice" }; 2const person2 = { name: "Bob" }; 3 4function greet(greeting, punctuation) { 5 console.log(`${greeting}, ${this.name}${punctuation}`); 6} 7 8// Use greet() with person1 as 'this' 9greet.call(person1, "Hello", "!"); // "Hello, Alice!" 10// Use greet() with person2 as 'this' 11greet.call(person2, "Hey", "!!"); // "Hey, Bob!!" 12

Analogy: You’re handing over your car keys (this) and a list of directions (arguments) to someone else. They drive the car right away.


apply(): Borrow a Function with an Array of Arguments

  • What it does: Similar to call, but accepts arguments as an array.
  • Syntax: func.apply(thisArg, [arg1, arg2, ...])

Example:

1const numbers = [5, 1, 4, 2, 3]; 2// Use Math.max with 'null' for 'this' (it doesn't matter here) 3const max = Math.max.apply(null, numbers); // 5 4

Why Use It?: Useful when working with functions that expect a list of arguments, but you have an array (e.g., Math.max).

Analogy: You give a box (array) of items instead of handing them one by one.


bind(): Create a Reusable Function with Fixed this

  • What it does: Returns a new function with a fixed this and optionally preset arguments.
  • Syntax: const boundFunc = func.bind(thisArg, arg1, arg2, ...)

Example:

1const person = { name: "Charlie" }; 2 3function logName() { 4 console.log(this.name); 5} 6 7// Create a new function where 'this' is always 'person' 8const boundLog = logName.bind(person); 9boundLog(); // "Charlie" 10

Partial Application: Partial application with bind allows you to create a new function with some preset arguments. This is helpful because it lets you reuse functions with specific configurations without rewriting them. For example, if you have a function that takes multiple arguments, you can use bind to fix some of those arguments, creating a simpler function that requires fewer inputs when called.

If you don't use bind, you would need to manually create a wrapper function to achieve the same effect. Here's an equivalent approach without using bind:

Without bind (Manual Partial Application)

1function multiply(a, b) { 2 return a * b; 3} 4// Manually creating a partially applied function 5function double(b) { 6 return multiply(2, b); 7} 8console.log(double(5)); // 10 (2 * 5) 9

This achieves the same result as using bind, but requires manually defining a new function.

If you don't use bind, you would need to manually create a wrapper function to achieve the same effect. Here's an equivalent approach without using bind:

Without bind (Manual Partial Application)

1function multiply(a, b) { 2 return a * b; 3} 4// Manually creating a partially applied function 5function double(b) { 6 return multiply(2, b); 7} 8 9console.log(double(5)); // 10 (2 * 5) 10

This achieves the same result as using bind, but requires manually defining a new function.

Using Closures (Partial Application)

1function multiply(a, b) { 2 return a * b; 3} 4 5// Using a closure to create a partially applied function 6function partialMultiply(a) { 7 return function (b) { 8 return multiply(a, b); 9 }; 10} 11 12const double = partialMultiply(2); 13 14console.log(double(5)); // Output: 10 (2 * 5) 15

With bind (Using Partial Application)

1function multiply(a, b) { 2 return a * b; 3} 4// Pre-set the first argument as 2 5const double = multiply.bind(null, 2); 6console.log(double(5)); // 10 (2 * 5) 7

Comparison of Methods

MethodDescription
Without bindYou manually create a new function
With bindBind pre-sets the first argument, returning a new function
With ClosuresA function returns another function that remembers the first argument (a)

4. Key Differences Summary

MethodInvokes Immediately?Arguments FormatReturns
call()YesIndividual (arg1, ...)Result of the function
apply()YesArray ([args])Result of the function
bind()NoIndividual (arg1, ...)A new bound function

Mnemonic:

  • Call → Comma-separated args.
  • Apply → Array of args.
  • Bind → Bind now, call later.

Corner Cases and Gotchas

Have you ever wondered about those tricky situations and unexpected surprises that can pop up when using these methods?


A. call and apply

  1. null/undefined as thisArg:

    • In non-strict mode, call/apply replace null/undefined with the global object (e.g., window in browsers).
    • In strict mode, this remains null/undefined.
    1function test() { 2 console.log(this); 3} 4test.call(null); // In non-strict: Window; strict: null 5
  2. Primitive Values as thisArg:

    • Primitives (e.g., 5, "hello") are wrapped into their object counterparts (Number, String).
    1function logType() { 2 console.log(typeof this); 3} 4logType.call(5); // "object" (Number(5)) 5
  3. Arrow Functions Ignore call/apply:

    • Arrow functions inherit this from their lexical scope. Using call/apply won’t override it.
    1const obj = { x: 10 }; 2const arrow = () => console.log(this.x); 3arrow.call(obj); // undefined (inherits global `this`) 4

B. bind

  1. Bound this Cannot Be Overridden:

    • Once bound, call/apply cannot change this for the bound function.
    1const boundFunc = function () { 2 console.log(this.id); 3}.bind({ id: 1 }); 4boundFunc.call({ id: 2 }); // 1 (not 2!) 5
  2. Partial Application with bind:

    • When you use bind to partially apply a function, the arguments you specify during binding are added before any arguments you pass when you later call the function.
    1function multiply(a, b) { 2 return a * b; 3} 4const triple = multiply.bind(null, 3); 5console.log(triple(4)); // 12 (3 * 4) 6
  3. Binding Constructors Fails:

    • Using bind on a constructor function doesn't work for binding this because when you use new, it creates a new instance, which overrides the bound this.
    1function Person(name) { 2 this.name = name; 3} 4const BoundPerson = Person.bind({ name: "Alice" }); 5const bob = new BoundPerson("Bob"); // Bob, not Alice! 6

2. Advanced Techniques and Applications


Method Borrowing

Use call/apply to borrow methods from other objects:

1// Borrow Array.prototype.map for array-like objects 2const arrayLike = { 0: "a", 1: "b", length: 2 }; 3const realArray = Array.prototype.map.call(arrayLike, (item) => 4 item.toUpperCase() 5); 6// ["A", "B"] 7

Currying with bind

Create specialized functions by pre-setting arguments:

1// Curried logger 2const log = console.log.bind(console, "[INFO]"); 3log("User logged in"); // [INFO] User logged in 4

Debouncing/Throttling with bind

Preserve context in event handlers:

1// Throttle a scroll handler 2function handleScroll() { 3 console.log(this.id); 4} 5const throttled = throttle(handleScroll.bind({ id: "scroll-container" }), 100); 6window.addEventListener("scroll", throttled); 7

Real-World Use Cases

  • Libraries/Frameworks: React uses bind in class components to bind event handlers.
  • Functional Programming: Currying with bind for reusable utility functions.
  • Polyfills: Shim older browsers using call/apply to mimic modern methods.