Skip to main content

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
  • call and apply run the function immediately; bind returns a new function with this locked in
  • The only difference between call and apply is argument format: separate values vs. an array
  • Decision rule: need to run now? Use call or apply. Need a reusable function with fixed context? Use bind
  • bind can also pre-fill arguments for later calls (partial application)

Quick example

javascript
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: call or apply
  • Spreading a dynamic array as function arguments (e.g., from arguments): apply
  • Passing a method as a callback and keeping this intact: bind
  • Pre-filling arguments for specialized versions of a function: bind
  • Fixing this loss in setTimeout, event listeners, or React class methods: bind

Comparison table

callapplybind
Executes immediatelyYesYesNo
Returns new functionNoNoYes
Argument formatSeparate valuesArraySeparate values (pre-filled)
Partial applicationNoNoYes
Works with newYesYesNo (TypeError)
Typical use caseBorrow method oncePass array as argsReusable 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

javascript
const obj = { x: 10 }; function log() { console.log(this.x); } log.bind(obj); // nothing happens - returns a function, does not call it

bind returns a new function. Call it separately: const bound = log.bind(obj); bound();

2. Passing a non-array to apply

javascript
function add(a, b) { return a + b; } add.apply(null, 1, 2); // TypeError: CreateListFromArrayLike called on non-object

apply 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

javascript
function show() { console.log(this.name); } const a = show.bind({ name: 'Alice' }); const b = a.bind({ name: 'Bob' }); b(); // "Alice" - the first bind wins, always

Once 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

javascript
function Counter(start) { this.count = start; } const BoundCounter = Counter.bind(null, 10); new BoundCounter(); // TypeError

Bound functions have no [[Construct]] support. Use a factory function if you need partial application with new.

5. bind(null) behaving differently in strict mode

javascript
'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 preserve this when 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)) so this stays 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

javascript
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

javascript
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

javascript
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 ready
Premium

A concise answer to help you respond confidently on this topic during an interview.

Finished reading?