Call, apply and bind methods in JavaScript
call, apply, and bind are methods on Function.prototype that let you set this explicitly for any function call, regardless of how that function was defined or where it lives.
Theory
TL;DR
- Analogy: handing a function a specific object as its "owner" for that call
callandapplyrun the function immediately;bindreturns a new function withthislocked in- The only difference between
callandapplyis argument format: separate values vs. an array - Decision rule: need to run now? Use
callorapply. Need a reusable function with fixed context? Usebind bindcan also pre-fill arguments for later calls (partial application)
Quick example
const user = { name: 'Alice' };
function greet(greeting, punctuation) {
console.log(`${greeting}, ${this.name}${punctuation}`);
}
greet.call(user, 'Hi', '!'); // "Hi, Alice!"
greet.apply(user, ['Hello', '.']); // "Hello, Alice."
const boundGreet = greet.bind(user, 'Hey');
boundGreet('!'); // "Hey, Alice!"call and apply fire immediately with the given this. bind returns a new function you call later, with this already set.
Key difference
call and apply invoke the function right away with a fixed this. The only distinction is how you pass arguments: call takes them one by one, apply takes an array. bind does neither of those immediately. It creates a new function object with this permanently set and optional pre-filled arguments, which you call whenever you need.
When to use
- Borrowing a method from one object for use on another:
callorapply - Spreading a dynamic array as function arguments (e.g., from
arguments):apply - Passing a method as a callback and keeping
thisintact:bind - Pre-filling arguments for specialized versions of a function:
bind - Fixing
thisloss insetTimeout, event listeners, or React class methods:bind
Comparison table
call | apply | bind | |
|---|---|---|---|
| Executes immediately | Yes | Yes | No |
| Returns new function | No | No | Yes |
| Argument format | Separate values | Array | Separate values (pre-filled) |
| Partial application | No | No | Yes |
Works with new | Yes | Yes | No (TypeError) |
| Typical use case | Borrow method once | Pass array as args | Reusable bound callback |
How it works internally
When you call fn.call(obj, arg), the engine sets [[ThisValue]] in the current call frame to obj before running fn. apply does the same but first unpacks the array into positional arguments. bind creates a new BoundFunction exotic object with a fixed [[BoundThis]] slot and a [[BoundArgs]] array. When you invoke the bound function later, the engine prepends those bound args, restores the fixed this, and delegates to the original. Bound functions have no [[Construct]] behavior, so passing one to new throws a TypeError.
Common mistakes
1. Expecting bind to run the function immediately
const obj = { x: 10 };
function log() { console.log(this.x); }
log.bind(obj); // nothing happens - returns a function, does not call itbind returns a new function. Call it separately: const bound = log.bind(obj); bound();
2. Passing a non-array to apply
function add(a, b) { return a + b; }
add.apply(null, 1, 2); // TypeError: CreateListFromArrayLike called on non-objectapply needs an array as its second argument. Fix: add.apply(null, [1, 2]) or switch to add.call(null, 1, 2).
3. Chaining bind and expecting to override context
function show() { console.log(this.name); }
const a = show.bind({ name: 'Alice' });
const b = a.bind({ name: 'Bob' });
b(); // "Alice" - the first bind wins, alwaysOnce this is locked by bind, no subsequent bind can change it. The [[BoundThis]] slot is set once and stays.
4. Using a bound function as a constructor
function Counter(start) { this.count = start; }
const BoundCounter = Counter.bind(null, 10);
new BoundCounter(); // TypeErrorBound functions have no [[Construct]] support. Use a factory function if you need partial application with new.
5. bind(null) behaving differently in strict mode
'use strict';
function fn() { console.log(this); }
fn.bind(null)(); // null (not the global object)In sloppy mode, null becomes the global object. In strict mode it stays null. Know which mode your code runs in before relying on this behavior.
I've seen the bind-in-constructor pattern in React class components across dozens of codebases. The moment someone forgets it and their event handler crashes with Cannot read property of undefined, it clicks immediately why bind exists at all.
Real-world usage
- React class components:
this.handleClick = this.handleClick.bind(this)in the constructor to preservethiswhen passing the method as a prop - Express:
app.use(handler.bind(dbContext))to inject a database connection into every route handler - DOM events:
element.addEventListener('click', handler.bind(component))sothisstays the component, not the DOM element - Partial application:
const double = multiply.bind(null, 2)to create specialized functions from general ones Math.max.apply(null, numbersArray)- the classic pre-spread pattern still found in legacy code
Follow-up questions
Q: What happens when you pass a primitive as thisArg to call?
A: In sloppy mode the primitive auto-boxes into its wrapper object (e.g., 5 becomes Number {5}). In strict mode it stays as the primitive. Avoid it in practice and use objects.
Q: Does chaining .bind and .call together let you override context?
A: No. fn.bind(obj).call(otherObj) still uses obj as this, not otherObj. bind wins. The locked this cannot be overridden by call, apply, or another bind.
Q: How would you write a bind polyfill?
A: Capture the original function and boundThis in a closure, then return a new function that calls the original via .apply(boundThis, boundArgs.concat(newArgs)). A strict polyfill also checks instanceof to throw when someone tries new on the result.
Q: Why does passing call, apply, or bind to an arrow function have no effect on this?
A: Arrow functions capture this lexically at definition time. They have no own [[ThisValue]]. Any thisArg you pass is ignored for this, though the arguments still get through normally.
Q: Can apply replace the spread operator when passing arrays as arguments?
A: Yes. fn.apply(null, arr) and fn(...arr) are functionally equivalent. Spread is cleaner in modern code, but apply still appears in legacy codebases and polyfill interview questions.
Examples
Basic: method borrowing
const dog = { sound: 'woof' };
const cat = { sound: 'meow' };
function makeNoise(times) {
for (let i = 0; i < times; i++) {
console.log(this.sound);
}
}
makeNoise.call(dog, 2); // "woof" "woof"
makeNoise.call(cat, 1); // "meow"One function, two objects as this. No code duplication. This is the core use case for call.
Intermediate: React class component with bind
class Button extends React.Component {
constructor(props) {
super(props);
// bind here so this.props works when the DOM event fires
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
console.log('Clicked:', this.props.label);
}
render() {
return <button onClick={this.handleClick}>{this.props.label}</button>;
}
}Without bind, passing this.handleClick as a prop detaches it from the instance. When the DOM event fires, this is undefined in strict mode. Binding in the constructor fixes it once and it holds across all renders.
Advanced: partial application via bind
function fetchData(baseURL, endpoint, id) {
console.log(`GET ${baseURL}${endpoint}/${id}`);
}
// lock in the base URL for all API calls
const apiCall = fetchData.bind(null, 'https://api.example.com');
// lock in the endpoint for user-specific calls
const getUser = apiCall.bind(null, '/users');
getUser(42); // "GET https://api.example.com/users/42"
getUser(99); // "GET https://api.example.com/users/99"Each bind pre-fills more arguments. this is null here because fetchData does not use it. The result is a chain of specialized functions built from one generic one, with no wrapper boilerplate.
Short Answer
Interview readyA concise answer to help you respond confidently on this topic during an interview.