Docs

5.5-Call-Apply-Bind

5.5 Call, Apply, and Bind

Overview

JavaScript provides three methods to explicitly control the this context of a function: call(), apply(), and bind(). These methods allow you to invoke functions with a specific context or create new functions with a permanently bound context.


Table of Contents

  1. •Understanding this Context
  2. •The call() Method
  3. •The apply() Method
  4. •call vs apply
  5. •The bind() Method
  6. •Practical Use Cases
  7. •Method Borrowing
  8. •Partial Application with bind
  9. •Common Patterns
  10. •Best Practices

Understanding this Context

The value of this depends on how a function is called.

ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”
│                   How 'this' is Determined               │
ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤
│                                                          │
│  Call Type                      'this' Value            │
│  ─────────────────────────────────────────────          │
│  Regular function call          global/undefined        │
│  Method call (obj.method())     obj                     │
│  Constructor (new Func())       new instance            │
│  call/apply/bind                explicitly set          │
│  Arrow function                 inherited (lexical)     │
│                                                          │
ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜

The Problem

const user = {
  name: 'Alice',
  greet() {
    console.log('Hello, ' + this.name);
  },
};

user.greet(); // "Hello, Alice"

const greet = user.greet;
greet(); // "Hello, undefined" - 'this' is lost!

The call() Method

Invokes a function with a specified this value and individual arguments.

Syntax

function.call(thisArg, arg1, arg2, ...)

Basic Example

function greet(greeting) {
  console.log(`${greeting}, ${this.name}!`);
}

const user1 = { name: 'Alice' };
const user2 = { name: 'Bob' };

greet.call(user1, 'Hello'); // "Hello, Alice!"
greet.call(user2, 'Hi'); // "Hi, Bob!"

With Multiple Arguments

function introduce(greeting, profession) {
  console.log(`${greeting}, I'm ${this.name}, a ${profession}.`);
}

const person = { name: 'Alice' };
introduce.call(person, 'Hello', 'developer');
// "Hello, I'm Alice, a developer."

Using call to Borrow Methods

const numbers = [1, 2, 3, 4, 5];

// Borrow max from Math
const max = Math.max.call(null, ...numbers);
console.log(max); // 5

// Convert array-like to array
function example() {
  const args = Array.prototype.slice.call(arguments);
  console.log(args);
}
example(1, 2, 3); // [1, 2, 3]

The apply() Method

Similar to call(), but takes arguments as an array.

Syntax

function.apply(thisArg, [argsArray])

Basic Example

function greet(greeting, punctuation) {
  console.log(`${greeting}, ${this.name}${punctuation}`);
}

const user = { name: 'Alice' };
greet.apply(user, ['Hello', '!']);
// "Hello, Alice!"

With Math Methods

const numbers = [5, 6, 2, 3, 7];

// Find max/min without spread
const max = Math.max.apply(null, numbers);
const min = Math.min.apply(null, numbers);

console.log(max); // 7
console.log(min); // 2

Dynamic Argument Passing

function log(message, ...args) {
  console.log(`[${this.level}] ${message}`, ...args);
}

const logger = { level: 'INFO' };
const args = ['User %s logged in', 'Alice'];

log.apply(logger, args);
// "[INFO] User Alice logged in"

call vs apply

ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”
│                  call() vs apply()                       │
ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤
│                                                          │
│  call(context, arg1, arg2, arg3)    // C = Comma        │
│  apply(context, [arg1, arg2, arg3]) // A = Array        │
│                                                          │
│  // Identical results:                                   │
│  fn.call(obj, 1, 2, 3);                                 │
│  fn.apply(obj, [1, 2, 3]);                              │
│                                                          │
│  // Modern alternative with spread:                      │
│  fn.call(obj, ...args);                                 │
│                                                          │
ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜

Choosing Between Them

Use call()Use apply()
Known number of argsDynamic array of args
Arguments are separateArguments in array
More readable for few argsLegacy/array operations
// When you know the arguments
greet.call(user, 'Hello', '!');

// When arguments are in an array
const args = ['Hello', '!'];
greet.apply(user, args);

// Modern: use spread with call
greet.call(user, ...args);

The bind() Method

Creates a new function with this permanently bound.

Syntax

const boundFn = function.bind(thisArg, arg1, arg2, ...)

Basic Example

const user = {
  name: 'Alice',
  greet() {
    console.log('Hello, ' + this.name);
  },
};

const greet = user.greet;
greet(); // "Hello, undefined"

const boundGreet = user.greet.bind(user);
boundGreet(); // "Hello, Alice"

Key Difference from call/apply

// call/apply invoke immediately
greet.call(user); // Invoked now

// bind returns a new function
const bound = greet.bind(user); // Returns function
bound(); // Invoked when called

Bind with Preset Arguments

function greet(greeting, punctuation) {
  console.log(`${greeting}, ${this.name}${punctuation}`);
}

const user = { name: 'Alice' };

// Bind with some arguments preset
const sayHello = greet.bind(user, 'Hello');
sayHello('!'); // "Hello, Alice!"
sayHello('?'); // "Hello, Alice?"

// Bind with all arguments preset
const fixedGreet = greet.bind(user, 'Hi', '!');
fixedGreet(); // "Hi, Alice!"

Practical Use Cases

1. Event Handlers

class Button {
  constructor(label) {
    this.label = label;
  }

  handleClick() {
    console.log(`${this.label} clicked`);
  }
}

const btn = new Button('Submit');

// Without bind - 'this' is the event target
button.addEventListener('click', btn.handleClick); // Wrong this

// With bind - 'this' is the Button instance
button.addEventListener('click', btn.handleClick.bind(btn));

2. setTimeout/setInterval

const counter = {
  count: 0,
  start() {
    // Without bind - 'this' would be undefined
    setInterval(
      function () {
        this.count++;
        console.log(this.count);
      }.bind(this),
      1000
    );
  },
};

// Alternative: arrow function
const counter2 = {
  count: 0,
  start() {
    setInterval(() => {
      this.count++;
      console.log(this.count);
    }, 1000);
  },
};

3. Callbacks

class UserService {
  constructor() {
    this.users = [];
  }

  fetchUsers() {
    fetch('/api/users')
      .then(this.handleResponse.bind(this))
      .catch(this.handleError.bind(this));
  }

  handleResponse(response) {
    // 'this' refers to UserService
    this.users = response.data;
  }

  handleError(error) {
    console.log('Error in', this.constructor.name);
  }
}

Method Borrowing

Use methods from one object on another.

Borrowing Array Methods

// arguments is array-like but not an array
function sum() {
  // Borrow forEach from Array.prototype
  let total = 0;
  Array.prototype.forEach.call(arguments, function (n) {
    total += n;
  });
  return total;
}

sum(1, 2, 3, 4, 5); // 15

Common Array Method Borrowing

const arrayLike = { 0: 'a', 1: 'b', 2: 'c', length: 3 };

// slice to convert to array
const arr = Array.prototype.slice.call(arrayLike);
console.log(arr); // ['a', 'b', 'c']

// join
const joined = Array.prototype.join.call(arrayLike, '-');
console.log(joined); // "a-b-c"

// Modern alternative
const arr2 = Array.from(arrayLike);
const arr3 = [...arrayLike]; // If iterable

Borrowing Object Methods

const obj = { a: 1, b: 2 };

// Borrow hasOwnProperty safely
const hasOwn = Object.prototype.hasOwnProperty;
console.log(hasOwn.call(obj, 'a')); // true
console.log(hasOwn.call(obj, 'toString')); // false

// Modern alternative
console.log(Object.hasOwn(obj, 'a')); // true

Borrowing String Methods

const str = 'Hello';

// Use array methods on string
const reversed = Array.prototype.reverse.call([...str]).join('');
console.log(reversed); // "olleH"

Partial Application with bind

Pre-fill some arguments of a function.

Basic Partial Application

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

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

console.log(double(5)); // 10
console.log(triple(5)); // 15

With Multiple Arguments

function greet(greeting, name, punctuation) {
  return `${greeting}, ${name}${punctuation}`;
}

const sayHello = greet.bind(null, 'Hello');
console.log(sayHello('Alice', '!')); // "Hello, Alice!"

const sayHelloToAlice = greet.bind(null, 'Hello', 'Alice');
console.log(sayHelloToAlice('!')); // "Hello, Alice!"

Practical Example: Logger

function log(level, message, ...data) {
  console.log(`[${level}] ${message}`, ...data);
}

const info = log.bind(null, 'INFO');
const error = log.bind(null, 'ERROR');
const debug = log.bind(null, 'DEBUG');

info('User logged in', { userId: 123 });
error('Connection failed', { host: 'localhost' });

Common Patterns

1. Constructor Binding Pattern

class Component {
  constructor() {
    this.state = { count: 0 };

    // Bind methods in constructor
    this.handleClick = this.handleClick.bind(this);
    this.handleChange = this.handleChange.bind(this);
  }

  handleClick() {
    this.state.count++;
  }

  handleChange(value) {
    console.log(this.state.count, value);
  }
}

2. Function Composition with call

function compose(...fns) {
  return function (value) {
    return fns.reduceRight((acc, fn) => fn.call(this, acc), value);
  };
}

3. Mixin Pattern with apply

const eventMixin = {
  on(event, handler) {
    this._events = this._events || {};
    this._events[event] = this._events[event] || [];
    this._events[event].push(handler);
  },
  emit(event, ...args) {
    if (this._events?.[event]) {
      this._events[event].forEach((fn) => fn.apply(this, args));
    }
  },
};

// Apply mixin to any object
Object.assign(user, eventMixin);
user.on('login', function () {
  console.log(this.name + ' logged in');
});
user.emit('login');

4. Borrowing Constructor

function Animal(name) {
  this.name = name;
}

function Dog(name, breed) {
  Animal.call(this, name); // Borrow Animal's constructor
  this.breed = breed;
}

const dog = new Dog('Rex', 'German Shepherd');
console.log(dog.name, dog.breed); // "Rex" "German Shepherd"

Best Practices

1. Use Arrow Functions for Simple Callbacks

// āŒ Verbose with bind
button.addEventListener('click', this.handleClick.bind(this));

// āœ… Simpler with arrow function
button.addEventListener('click', () => this.handleClick());

2. Bind in Constructor for Class Methods

class Component {
    constructor() {
        // āœ… Bind once in constructor
        this.handleClick = this.handleClick.bind(this);
    }
}

// āŒ Don't bind in render/every call
render() {
    button.onclick = this.handleClick.bind(this);  // New function each time
}

3. Use null for thisArg When Not Needed

// When 'this' doesn't matter
const max = Math.max.apply(null, numbers);
const double = multiply.bind(null, 2);

4. Prefer Spread Over apply for Arrays

// āŒ Old way
Math.max.apply(null, numbers);

// āœ… Modern way
Math.max(...numbers);

5. Be Careful with Multiple Binds

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

const bound1 = greet.bind({ name: 'Alice' });
const bound2 = bound1.bind({ name: 'Bob' }); // Ignored!

bound2(); // "Alice" - first bind wins

Summary

ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”
│           call, apply, bind Quick Reference              │
ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤
│                                                          │
│  call(context, arg1, arg2)                              │
│  • Invokes immediately                                   │
│  • Arguments as comma-separated list                     │
│                                                          │
│  apply(context, [args])                                 │
│  • Invokes immediately                                   │
│  • Arguments as array                                    │
│                                                          │
│  bind(context, arg1, arg2)                              │
│  • Returns new bound function                            │
│  • Can preset arguments (partial application)            │
│  • 'this' permanently bound                             │
│                                                          │
│  Common Uses:                                            │
│  • Method borrowing                                      │
│  • Event handler binding                                 │
│  • Callback context preservation                         │
│  • Partial application                                   │
│  • Constructor borrowing                                 │
│                                                          │
│  Memory Tip:                                             │
│  • Call = Comma (individual arguments)                   │
│  • Apply = Array (arguments in array)                    │
│                                                          │
ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜

Next Steps

  • •Practice with the examples in examples.js
  • •Complete the exercises in exercises.js
  • •Learn about closures and scope
  • •Explore prototypes and inheritance
.5 Call Apply Bind - JavaScript Tutorial | DeepML