JS bind, call, and apply Methods: Full Tutorial

A complete tutorial on JavaScript's bind, call, and apply methods. Learn how each one sets the this context, the key differences between them, practical use cases including method borrowing and partial application, and when to use each in modern JavaScript.

JavaScriptintermediate
12 min read

call, apply, and bind are all methods on Function.prototype that let you explicitly set the this context when calling a function. Understanding them is fundamental to mastering JavaScript's this keyword and enables powerful techniques like method borrowing and partial application.

The Three Methods at a Glance

javascriptjavascript
function greet(greeting, punctuation) {
  return `${greeting}, I'm ${this.name}${punctuation}`;
}
 
const alice = { name: "Alice" };
 
// call: invoke immediately, arguments listed one by one
greet.call(alice, "Hello", "!");     // "Hello, I'm Alice!"
 
// apply: invoke immediately, arguments as an array
greet.apply(alice, ["Hi", "?"]);     // "Hi, I'm Alice?"
 
// bind: does NOT invoke — returns a new function with this bound
const aliceGreet = greet.bind(alice);
aliceGreet("Hey", ".");              // "Hey, I'm Alice."
MethodInvokes ImmediatelyArgumentsReturns
call(thisArg, ...args)YesSpreadReturn value of function
apply(thisArg, argsArray)YesArrayReturn value of function
bind(thisArg, ...args)NoPre-filled (optional)New bound function

call() in Depth

call() invokes the function immediately with a specified this and individual arguments:

javascriptjavascript
function introduce(role, company) {
  return `${this.name} — ${role} at ${company}`;
}
 
const dev = { name: "Sam" };
const mgr = { name: "Dana" };
 
introduce.call(dev, "Developer", "Acme Corp"); // "Sam — Developer at Acme Corp"
introduce.call(mgr, "Manager",   "Globex");    // "Dana — Manager at Globex"

Method Borrowing With call()

call() enables calling a method designed for one object type on a different object:

javascriptjavascript
// Borrow Array methods for array-like objects
function logArgs() {
  // arguments is array-like but lacks Array methods
  const arr = Array.prototype.slice.call(arguments);
  // OR: Array.from(arguments) in modern code
  return arr.join(", ");
}
 
logArgs("a", "b", "c"); // "a, b, c"
 
// Check type with Object.prototype.toString
const type = (val) => Object.prototype.toString.call(val).slice(8, -1);
type([]);          // "Array"
type(null);        // "Null"
type(new Date());  // "Date"
type(/regex/);     // "RegExp"

apply() in Depth

apply() is identical to call() but takes arguments as an array:

javascriptjavascript
function sum(...nums) {
  return nums.reduce((a, b) => a + b, 0);
}
 
const numbers = [5, 10, 3, 8, 2];
sum.apply(null, numbers); // 28 — spread the array as args
// Modern equivalent: sum(...numbers) — spread syntax is usually preferred

Finding Min/Max of an Array

A classic pattern before spread syntax:

javascriptjavascript
const scores = [78, 92, 85, 61, 99, 73];
 
Math.max.apply(null, scores); // 99
Math.min.apply(null, scores); // 61
 
// Modern ES6 equivalent (preferred):
Math.max(...scores); // 99
Math.min(...scores); // 61

apply is still relevant when you receive arguments already in an array and cannot easily use spread (e.g., dynamic function arguments).

bind() in Depth

bind() returns a new function that permanently has this set to the provided value. The original function is not called or modified:

javascriptjavascript
class EventBus {
  constructor() {
    this.listeners = [];
  }
 
  subscribe(fn) {
    this.listeners.push(fn);
  }
 
  emit(event) {
    this.listeners.forEach(fn => fn(event));
  }
}
 
class Notification {
  constructor(name) {
    this.name = name;
    this.count = 0;
  }
 
  handleEvent(event) {
    this.count += 1;
    console.log(`${this.name} received: ${event} (#${this.count})`);
  }
}
 
const bus = new EventBus();
const notif = new Notification("AlertBox");
 
// Without bind: this would be undefined inside handleEvent
bus.subscribe(notif.handleEvent.bind(notif));
bus.emit("login");  // "AlertBox received: login (#1)"
bus.emit("logout"); // "AlertBox received: logout (#2)"

Partial Application With bind()

bind() also pre-fills arguments (partial application):

javascriptjavascript
function multiply(a, b) {
  return a * b;
}
 
const double  = multiply.bind(null, 2);  // a = 2, b = supplied later
const triple  = multiply.bind(null, 3);
const tenTimes = multiply.bind(null, 10);
 
double(5);    // 10
triple(5);    // 15
tenTimes(7);  // 70
 
// More practical example
function log(level, message) {
  console.log(`[${level.toUpperCase()}] ${message}`);
}
 
const info  = log.bind(null, "info");
const warn  = log.bind(null, "warn");
const error = log.bind(null, "error");
 
info("Server started");       // "[INFO] Server started"
warn("Rate limit approached"); // "[WARN] Rate limit approached"
error("Connection refused");   // "[ERROR] Connection refused"

Passing null or undefined as thisArg

When the function does not use this, pass null or undefined as the first argument. In strict mode, this will be null/undefined; in sloppy mode, it falls back to the global object:

javascriptjavascript
// Pure function — doesn't use this: null is conventional
const nums = [3, 1, 4, 1, 5, 9];
nums.sort.call(null, (a, b) => a - b); // Unusual — just for illustration
Math.max.apply(null, nums);            // 9 — null is conventional here

Hard Binding With bind — Cannot Be Rebound

Once bound with bind(), the this value cannot be overridden again:

javascriptjavascript
function whatIsThis() { return this.x; }
 
const bound = whatIsThis.bind({ x: 1 });
console.log(bound());                         // 1
console.log(bound.call({ x: 999 }));          // Still 1 — cannot rebind
console.log(bound.apply({ x: 999 }));         // Still 1
console.log(bound.bind({ x: 999 })());        // Still 1 — re-binding a bound function is a no-op

call vs apply — When to Use Which

In modern JavaScript with spread syntax, call and apply are largely interchangeable. The practical rule:

javascriptjavascript
// When you have discrete arguments — call
fn.call(ctx, arg1, arg2, arg3);
 
// When you have an array of arguments — apply (pre-ES6) or call + spread (ES6+)
fn.apply(ctx, argsArray);
fn.call(ctx, ...argsArray); // Modern equivalent — preferred

For more guidance on choosing between them, see when to use js bind vs call vs apply.

Rune AI

Rune AI

Key Insights

  • call invokes with individual args: fn.call(thisArg, arg1, arg2) — triggers the function immediately with this set to thisArg
  • apply invokes with an array: fn.apply(thisArg, [arg1, arg2]) — same as call but takes an array; largely replaced by spread syntax in modern code
  • bind returns a new permanently-bound function: Does not call the function; returns a new function where this is locked in and cannot be rebound by subsequent call/apply/bind
  • bind enables partial application: Pre-filled arguments in bind(null, a) create specialized versions of functions — a functional programming technique
  • Bound this cannot be overridden: A bound function's this ignores call/apply/bind attempts to change it; only new binding supersedes it
RunePowered by Rune AI

Frequently Asked Questions

Does bind create a performance cost?

`bind()` creates a new function object. For methods bound in a constructor, this is a one-time cost per instance. For methods bound in render loops (e.g., `onClick={this.handler.bind(this)}` in React), a new function is created every render — prefer class field arrows or binding in the constructor for performance.

Can I use bind for constructor functions?

`bind()` works on constructors but the bound `this` is ignored when the function is called with `new` — `new` binding always takes priority over `bind`.

What is the difference between bind and a closure?

Both produce a new function with a captured value. `bind` specifically captures `this` (and optionally pre-fills arguments). A closure captures any local variable. An arrow function is essentially both — it captures `this` and any outer variables via closure.

Is there a performance difference between call and apply?

Historically, `apply` was slightly slower because of the extra array unpacking. In modern engines, the difference is negligible. Use whichever is more convenient for your argument structure.

Conclusion

call, apply, and bind are the tools for explicit this binding in JavaScript. call invokes immediately with listed arguments, apply invokes immediately with an array, and bind returns a permanently-bound new function without invoking. bind also enables partial application by pre-filling arguments. These methods are the foundation of solving this context loss in callbacks and enable powerful patterns like method borrowing from built-in prototypes.