Understanding call, apply, and bind in JavaScript

Hero image for Understanding call, apply, and bind in JavaScript. Image by Lewis Kang'ethe Ngugi.
Hero image for 'Understanding call, apply, and bind in JavaScript.' Image by Lewis Kang'ethe Ngugi.

In Brief

call, apply and bind all control the this value of a JavaScript function. call invokes the function with individual arguments, apply invokes it with an arraylike argument list and bind returns a new function with context or arguments fixed for later use.

JavaScript's this keyword still catches people out because it is decided by how a function is called, not where the function is written.

That distinction matters in real code reviews. You might see call, apply or bind in legacy modules, event handlers, callbacks, decorators, functional utilities, tests, mocks or thirdparty library integrations. Modern JavaScript has reduced the need for manual binding in many daytoday cases, but it has not removed the need to understand context.

The mistake to avoid is using bind as a quick patch without asking why the context was lost. Sometimes binding is exactly the right fix. Sometimes it hides a muddled component boundary, an extracted method being passed around too freely or a callback that should be an arrow function.


What call, apply and bind do

All three methods live on JavaScript functions. They let you control the this value explicitly.

The difference is when the function runs and how its arguments are supplied.

call

call invokes the function immediately. You pass the this value first, then each argument separately.

function greet(greeting, name) {  console.log(`${greeting}, ${name}! My context is:`, this);}greet.call({ role: 'developer' }, 'Hello', 'Maddie');// Output: Hello, Maddie! My context is: { role: 'developer' }

apply

apply also invokes the function immediately, but it expects the arguments as an array or arraylike object.

const numbers = [5, 10, 15];const max = Math.max.apply(null, numbers);console.log(max);  // Output: 15

bind

bind does not run the function straight away. It returns a new function with the chosen this value fixed for later.

const user = { name: 'Sophie' };function introduce() {  console.log(`My name is ${this.name}`);}const boundIntroduce = introduce.bind(user);boundIntroduce();  // Output: My name is Sophie

That makes bind useful when you need a function reference, not an immediate result.


call in practice

Use call when you want to run a function now and provide the context directly.

It is useful for borrowed methods, arraylike objects and small cases where explicit invocation is clearer than wrapping the function in another function.

Syntax of call

functionName.call(thisArg, arg1, arg2, ...);
  • thisArg: The value to use as this inside the function.
  • arg1, arg2, ...: The arguments passed to the function one by one.

Borrowing methods with call

Borrowing a method is the classic example. The method belongs to one object or prototype, but you want to run it against another value.

const person = {  firstName: 'John',  lastName: 'Doe',  fullName: function () {    return `${this.firstName} ${this.lastName}`;  },};const anotherPerson = { firstName: 'Jane', lastName: 'Smith' };// Borrowing fullName method for anotherPersonconsole.log(person.fullName.call(anotherPerson));  // Output: "Jane Smith"

This pattern appears less often in modern application code than it used to, but it still appears around arraylike values, DOM APIs and old utility code.

Passing arguments with call

When the arguments are already known, call keeps the invocation direct.

function greet(greeting, punctuation) {  console.log(`${greeting}, ${this.name}${punctuation}`);}const user = { name: 'Ellie' };// Invoking greet with a custom `this` and argumentsgreet.call(user, 'Hello', '!');  // Output: "Hello, Ellie!"

What to watch with call

  • thisArg becomes undefined in strict mode when it is null or undefined.
  • Outside strict mode, the fallback can be the global object, which is rarely what you want.
  • If the argument list is already an array, apply or spread syntax may be clearer.

apply in practice

apply is close to call, but the argument list is supplied as an arraylike value.

That makes it useful when a function receives or constructs arguments dynamically.

Syntax of apply

functionName.apply(thisArg, [arg1, arg2, ...]);
  • thisArg: The value to use as this inside the function.
  • [arg1, arg2, ...]: An array or arraylike object containing the arguments.

Borrowing methods with apply

apply can borrow methods in the same way as call. The difference is the argument shape.

const numbers = [5, 10, 15, 20];const maxNumber = Math.max.apply(null, numbers);console.log(maxNumber);  // Output: 20

In this example, apply makes it easy to pass the array of numbers to Math.max.

Another apply example

function introduce(greeting, punctuation) {  console.log(`${greeting}, my name is ${this.name}${punctuation}`);}const person = { name: 'Ellie' };const args = ['Hello', '!'];introduce.apply(person, args);  // Output: "Hello, my name is Ellie!"

What to watch with apply

  • Use apply when the arguments are already arraylike.
  • For ordinary arrays, spread syntax is often easier to read in modern JavaScript.
  • Do not use apply just to look clever. Explicit code usually wins in production reviews.

bind in practice

bind creates a new function with this fixed.

That is useful when a function will be called later by something else, such as an event listener, timer, callback or library API.

Syntax of bind

const boundFunction = functionName.bind(thisArg, arg1, arg2, ...);
  • thisArg: The value to use as this inside the new function.
  • arg1, arg2, ...: Optional arguments that are preset when the new function is called.

Preserving context with bind

const user = {  name: 'Maddie',  greet: function () {    console.log(`Hello, ${this.name}!`);  },};const greetUser = user.greet.bind(user);// Even if `greetUser` is called in a different context, `this` is preservedgreetUser();  // Output: "Hello, Maddie!"

This is the use case most developers recognise: a method is passed around, and you still need it to run with the original object as this.

Presetting arguments with bind

bind can also preset arguments. That creates a specialised function from a more general one.

function multiply(a, b) {  return a * b;}const double = multiply.bind(null, 2);  // `a` is pre-set to 2console.log(double(5));  // Output: 10

In this example, bind creates double by fixing the first argument of multiply to 2. Calling double(5) then calls multiply(2, 5).

What to watch with bind

  • A bound function is a new function reference.
  • Binding inside render paths or repeated setup code can create avoidable churn.
  • If an arrow function would make the ownership clearer, prefer the clearer option.
  • Do not use binding to hide a confused object model.

When to Use Each One

The short version is straightforward.

Use call for immediate invocation with listed arguments

const person = {  name: 'John',};function introduce(age) {  console.log(`Hi, I'm ${this.name} and I'm ${age} years old.`);}introduce.call(person, 30);// Output: Hi, I'm John and I'm 30 years old.

call is useful when you want the invocation to be explicit and the argument list is known.

Use apply for immediate invocation with arraylike arguments

const numbers = [5, 1, 7, 3];const maxNumber = Math.max.apply(null, numbers);console.log(maxNumber);  // Output: 7

apply is useful when the arguments already exist as a list. In modern code, compare it with spread syntax before choosing it.

Use bind for delayed invocation

const printer = {  prefix: 'Message:',};function printMessage(message) {  console.log(`${this.prefix} ${message}`);}const boundPrinter = printMessage.bind(printer);boundPrinter('Hello, World!');// Output: Message: Hello, World!

bind is useful when another part of the system will call the function later and you need the context or initial arguments to stay fixed.


The Production Caveat

Modern arrow functions and class fields mean you will often write less manual binding than older JavaScript articles suggest. That is a good thing.

The caveat is that production code still contains older patterns, library callbacks and framework integration points. Understanding call, apply and bind helps you read that code without guessing.

It also helps you avoid the lazy fix. If this changes unexpectedly, first ask how the function is being called. The related article on why `this` changes in JavaScript event handlers and methods covers that failure mode directly.

Use these methods when they make invocation clearer. Avoid them when they make the code look more advanced than the problem deserves.


Have a complex web platform issue?

Tell me what is blocked, what has changed, and what needs to be true after the fix. I'll come back with a practical next step.