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.
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
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."| Method | Invokes Immediately | Arguments | Returns |
|---|---|---|---|
call(thisArg, ...args) | Yes | Spread | Return value of function |
apply(thisArg, argsArray) | Yes | Array | Return value of function |
bind(thisArg, ...args) | No | Pre-filled (optional) | New bound function |
call() in Depth
call() invokes the function immediately with a specified this and individual arguments:
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:
// 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:
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 preferredFinding Min/Max of an Array
A classic pattern before spread syntax:
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); // 61apply 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:
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):
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:
// 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 hereHard Binding With bind — Cannot Be Rebound
Once bound with bind(), the this value cannot be overridden again:
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-opcall vs apply — When to Use Which
In modern JavaScript with spread syntax, call and apply are largely interchangeable. The practical rule:
// 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 — preferredFor more guidance on choosing between them, see when to use js bind vs call vs apply.
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
Frequently Asked Questions
Does bind create a performance cost?
Can I use bind for constructor functions?
What is the difference between bind and a closure?
Is there a performance difference between call and apply?
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.
More in this topic
OffscreenCanvas API in JS for UI Performance
Master the OffscreenCanvas API to offload rendering from the main thread. Covers worker-based 2D and WebGL rendering, animation loops inside workers, bitmap transfer, double buffering, chart rendering pipelines, image processing, and performance measurement strategies.
Advanced Web Workers for High Performance JS
Master Web Workers for truly parallel JavaScript execution. Covers dedicated and shared workers, structured cloning, transferable objects, SharedArrayBuffer with Atomics, worker pools, task scheduling, Comlink RPC patterns, module workers, and performance profiling strategies.
JavaScript Macros and Abstract Code Generation
Master JavaScript code generation techniques for compile-time and runtime metaprogramming. Covers AST manipulation, Babel plugin authorship, tagged template literals as macros, code generation pipelines, source-to-source transformation, compile-time evaluation, and safe eval alternatives.