Technology · JavaScript

Advanced

JavaScript Call Apply Bind

Control the this keyword explicitly using call, apply, and bind on any function.

TL;DR
  1. 01Call and apply invoke a function with a chosen this value.
  2. 02Apply takes arguments as an array; call takes a list.
  3. 03Bind returns a new function with this permanently fixed.

Why this Needs Explicit Control

  • The value of this depends on how a function is called, not where it's defined.
    const user = { name: 'Ada', greet() { return `Hi, ${this.name}`; } };
    const fn = user.greet;
    fn(); // 'Hi, undefined' — this lost its connection to user
    
  • Passing a method as a value, like a callback, detaches it from its original object.
  • call, apply, and bind exist to set this explicitly regardless of call site.
  • All three live on Function.prototype, so every function has access to them.
  • Without explicit control, callbacks and event handlers commonly break on this.
  • Understanding these three methods is essential for working with older, non-arrow-function code.

Using call()

  • Use .call(thisArg, arg1, arg2, ...) to invoke a function immediately with arguments listed individually.
    function greet(greeting) { return `${greeting}, ${this.name}`; }
    greet.call({ name: 'Ada' }, 'Hi'); // 'Hi, Ada'
    
  • The first argument becomes this inside the function for that one call.
  • Remaining arguments map positionally to the function's parameters.
  • Use call() to borrow a method from one object and run it against another.
    const max = Math.max.call(null, 1, 5, 3); // 5
    
  • Passing null or undefined as thisArg uses the global object in non-strict mode.
  • call() does not change the original function; it only affects that single invocation.

Using apply()

  • Use .apply(thisArg, argsArray) exactly like call(), but pass arguments as one array.
    function sum(a, b, c) { return a + b + c; }
    sum.apply(null, [1, 2, 3]); // 6
    
  • apply() is the better choice when arguments already exist as an array or array-like.
    Math.max.apply(null, [4, 8, 2]); // 8
    
  • Modern code often replaces apply() with the spread operator: Math.max(...nums).
  • apply() still matters when forwarding an arguments object between functions.
  • Both call() and apply() execute the function synchronously and return its result.
  • Choosing between them is purely about argument shape — list versus array.

Using bind()

  • Use .bind(thisArg) to create a new function with this permanently fixed.
    const user = { name: 'Ada', greet() { return `Hi, ${this.name}`; } };
    const boundGreet = user.greet.bind(user);
    boundGreet(); // 'Hi, Ada' — works even detached from user
    
  • Unlike call() and apply(), bind() does not invoke the function immediately.
  • The returned function can be stored, passed around, and called later safely.
  • Bind methods in a constructor so they keep this when used as callbacks.
    class Button {
      constructor() { this.onClick = this.onClick.bind(this); }
      onClick() { console.log(this); }
    }
    
  • Calling bind() again on an already-bound function cannot change its fixed this.
  • Bound functions report 'bound functionName' when inspected, which helps when debugging stacks.

Partial Application with bind

  • Arguments passed to bind() after thisArg get permanently prepended to every future call.
    function multiply(a, b) { return a * b; }
    const double = multiply.bind(null, 2);
    double(5); // 10
    
  • This technique is called partial application, fixing some arguments ahead of time.
  • Combine partial application with event handlers to pass extra context cleanly.
    button.addEventListener('click', handleClick.bind(null, itemId));
    
  • Partially applied functions still accept additional arguments at call time.
  • Use partial application to build specialized utility functions from general ones.
  • This pattern avoids writing repetitive wrapper functions for common argument combinations.
Tips
  1. 01Use bind() when passing a method as a callback or event handler, so this stays correct.
  2. 02Reach for apply() when arguments already exist as an array, such as forwarding arguments between wrapper functions.
Warnings
  1. 01Calling bind() repeatedly on the same function creates a new wrapper each time, which breaks reference equality checks like removeEventListener.
  2. 02Arrow functions ignore call, apply, and bind for this, since arrow functions always inherit this from their enclosing scope.
FAQ
JavaScript Async IteratorsJavaScript Classes