call, apply, and bind
Explicit this control — call vs apply vs bind, partial application, bound constructors, and when arrows make bind pointless.
- javascript
- call
- apply
- bind
- this
call, apply, and bind are how you set this on purpose for ordinary functions. They are not “alternatives to calling a function.” They are the explicit-binding rule in the this binding hierarchy.
fn.call(thisArg, ...args)— invokefnnow with giventhisand arguments.fn.apply(thisArg, argsArray)— same, args as an array-like.fn.bind(thisArg, ...partialArgs)— return a new function withthis(and optional leading args) fixed.
If you treat them as interchangeable syntax sugar, you’ll misuse bind in hot paths and forget that arrows ignore them for this.
The problem they solve
Methods detach from their object the moment you pass them as a callback:
const user = {
name: 'Ada',
greet(prefix) {
return `${prefix}, ${this.name}`;
},
};
console.log(user.greet('Hi')); // "Hi, Ada"
const loose = user.greet;
// loose('Hi'); // TypeError or wrong this — depends on strict mode / environment
DOM handlers, setTimeout, React props (class era), and array methods all strip the method call shape obj.method(). Explicit binding puts this back.
call — invoke now, list of args
function intro(greeting, punctuation) {
return `${greeting}, ${this.role}${punctuation}`;
}
const person = { role: 'engineer' };
intro.call(person, 'Hello', '!'); // "Hello, engineer!"
Use call when you already have discrete arguments. Also common when borrowing methods:
const arrayLike = { 0: 'a', 1: 'b', length: 2 };
const joined = Array.prototype.join.call(arrayLike, '-'); // "a-b"
Modern code often prefers Array.from(arrayLike).join('-') or spread when the value is iterable — but call still shows up in polyfills, interview tasks, and performance-sensitive borrowing.
apply — invoke now, args as array
const nums = [3, 9, 1];
Math.max.apply(null, nums); // 9
// Today:
Math.max(...nums);
Historically apply was how you spread before rest/spread existed. It still matters when:
- You are implementing utilities that forward unknown arity
- You must pass a true array-like without converting first
- Interview whiteboards ban
...to force fundamentals
function logAll() {
const args = Array.prototype.slice.apply(arguments);
console.log(args);
}
Prefer rest parameters in new functions: function logAll(...args).
bind — return a bound function for later
const user = {
name: 'Grace',
greet(greeting) {
return `${greeting}, ${this.name}`;
},
};
const greetGrace = user.greet.bind(user, 'Hey');
greetGrace(); // "Hey, Grace"
setTimeout(user.greet.bind(user, 'Later'), 0);
bind does not call greet. It returns a new exotic function whose this is permanently user (for ordinary calls). Partial arguments are prepended:
function multiply(a, b) {
return a * b;
}
const double = multiply.bind(null, 2);
double(5); // 10
bind vs re-wrapping
// bind
const b = user.greet.bind(user);
// manual wrap — similar this, different function identity each time you recreate
const w = (...args) => user.greet(...args);
Bound functions have a stable identity if you bind once and store the result. Re-creating bind every render (React class handlers) forces child pure-component props to change. Store bound methods once (constructor bind, class fields, or hooks patterns).
Precedence: bound functions are sticky
function show() {
return this.id;
}
const objA = { id: 'A' };
const objB = { id: 'B' };
const boundA = show.bind(objA);
boundA.call(objB); // "A" — call cannot override bind's this
boundA.apply(objB); // "A"
Exception people remember for interviews: new boundFn() uses the new object as this for the underlying constructor path; bound thisArg is ignored under new. Rare in app code; common in trivia.
Soft bind / nullish thisArg (strict vs sloppy)
In sloppy mode, call/apply/bind with null or undefined as thisArg coerce this to the global object. In strict mode (and modules, which are strict), this stays null/undefined.
'use strict';
function f() {
return this;
}
f.call(null); // null
Don’t rely on global coercion. Pass the object you mean.
Arrow functions ignore this binding
const obj = {
id: 1,
regular() {
return this.id;
},
arrow: () => this.id, // lexical this from outer scope, not obj
};
obj.regular(); // 1
obj.arrow(); // likely undefined in modules
const bound = obj.arrow.bind({ id: 99 });
bound(); // still outer lexical this — bind does not rebind arrow `this`
Arrows close over this from the enclosing scope (closures + lexical this). Use arrows when you want that. Use ordinary functions + bind/call when the method must take this from the receiver.
Implementing a tiny bind (interview staple)
Function.prototype.myBind = function (thisArg, ...boundArgs) {
const original = this;
if (typeof original !== 'function') {
throw new TypeError('Bind must be called on a function');
}
return function bound(...args) {
return original.apply(thisArg, boundArgs.concat(args));
};
};
function greet(g, p) {
return `${g}, ${this.name}${p}`;
}
const hi = greet.myBind({ name: 'Lin' }, 'Hi');
hi('!'); // "Hi, Lin!"
A production-faithful bind also handles new, prototype linkage, and length/name. Interviewers usually want: store thisArg, prepend args, use apply, return a new function. Mentioning real bind edge cases scores extra points without implementing them.
When to use which
| Tool | Use when |
|---|---|
call |
One-shot invoke with known args; method borrowing |
apply |
One-shot invoke with array of args; legacy spread |
bind |
Callback needs fixed this or partial args later |
| Arrow | Want lexical this; no dynamic receiver |
Direct obj.method() |
You still own the call shape |
Debounce/throttle utilities use apply so wrapped methods keep the caller’s this — see Debounce Implementation and Throttle Implementation.
Footguns
bindevery render / every event attach — new function identity; hard to removeEventListener.- Binding arrows — no-op for
this; confuses readers. - Forgetting partial args are leading —
fn.bind(null, a)fixes first parameter. applywith huge arrays — older engines had argument count limits; spread/Math.maxreductions can share that history.- Assuming
bindcopies the function’s prototype methods you care about — you get a bound wrapper, not a clone of the function object’s own properties (except special bound exotic behavior).
Interview angle
Prompt: “Difference between call, apply, and bind?”
Strong answer: “call and apply invoke the function immediately with an explicit this; call takes a argument list, apply takes an array. bind returns a new function with this and optional leading arguments fixed for later calls. Bound this wins over later call/apply. Arrow functions don’t get their this from these APIs.”
Code task: Implement bind with partial args; explain why array.map(obj.method) breaks and how obj.method.bind(obj) fixes it.
Related on this site
- this Binding Rules — full priority order including
new - Closures — lexical vs dynamic
this - Debounce Implementation —
applyin wrappers - Throttle Implementation — same pattern
- Prototypal Inheritance — why methods live on shared prototypes
Further reading
- Function.prototype.call — MDN
- Function.prototype.apply — MDN
- Function.prototype.bind — MDN
- javascript.info — Function binding
Related guides
- this Binding RulesHow JavaScript decides this — default, method, explicit call/apply/bind, new, and lexical this in arrows. With runnable examples.
- Arrow Functions Deep DiveLexical this, no arguments object, no construct, concise bodies — when arrows help and when methods and generators need classic functions.
- AbortControllerCancel fetch and other async work with AbortController and AbortSignal — timeouts, race conditions, and cleanup when components unmount.
- Array find, some, every, includesShort-circuiting array predicates: find, findIndex, some, every, and includes — when to use each and common interview traps.
- Array map, filter, reducemap, filter, and reduce as the core transform toolkit — immutability, chaining costs, reduce patterns, and when a plain loop is clearer.