Your country

Tools that support it use your country for local currency, number formats, units and paper size. Your choice is saved only in this browser.

Type a name or a two-letter code. Use the up and down arrow keys to move through the countries, Enter to choose one and Escape to close.

JavaScript (ES2026)  Module 5 – Objects in depth

call, apply and bind

Call JavaScript functions with an explicit this using call and apply, make bound functions with bind for partial application, and see how bind meets new.

  • Intermediate
  • 18 minutes
  • Examples run with Node.js 24.21.0 and quickjs 0.32.0
  • By MySmartCoPilot
Free lesson, Pro answers: 18 minutes
Free: the whole lesson. With a pass: the model answers to its 2 interview questions.

What you will learn

  • Call a function with an explicit this using call and apply
  • Create bound functions with bind, including partial application
  • Explain how bind interacts with new and arrow functions

Before you start

What you’ll learn · Before you start

What you will learn

  • Call a function with an explicit this using call and apply
  • Create bound functions with bind, including partial application
  • Explain how bind interacts with new and arrow functions

Before you start

On this page

Every JavaScript function has three methods that let you choose its this instead of leaving it to the shape of the call. call runs the function at once with the this you give and the arguments listed one by one: describe.call(asha, 'Hello', '!'). apply does the same but takes the arguments as an array: describe.apply(asha, ['Hello', '!']). bind runs nothing: it returns a new function whose this, and optionally its first arguments, are fixed for good, so describe.bind(asha, 'Hello') can be passed around as a callback and still greet Asha. call and apply are for one call with a borrowed or chosen this; bind is for a function you hand to someone else. Two exceptions matter: calling a bound function with new ignores its bound this, and an arrow function ignores the this of all three. This lesson runs each method, shows how this values are converted, borrows array methods, compares apply with spread, and builds bind from scratch in the exercise.

call and apply: choose this for one call

One function, two objects, both ways of passing arguments JavaScript · explicit-this.mjs
// call and apply run a function once, with the this you choose. They differ only in how the arguments are passed.
function describe(greeting, punctuation) {
  return `${greeting}, ${this.name}${punctuation}`;
}

const asha = { name: 'Asha' };
const ravi = { name: 'Ravi' };

console.log(describe.call(asha, 'Hello', '!')); // the arguments one by one
console.log(describe.apply(ravi, ['Namaste', '.'])); // the arguments in an array
console.log(describe.apply(asha, { 0: 'Hi', 1: '?', length: 2 })); // or in an array-like object

// A method of one object, run with another object as this.
const shop = {
  name: 'Corner Shop',
  label() {
    return `Shop: ${this.name}`;
  },
};
console.log(shop.label(), '|', shop.label.call(ravi));

Output

Hello, Asha!
Namaste, Ravi.
Hi, Asha?
Shop: Corner Shop | Shop: Ravi

Recorded with Node.js 24.21.0 on macOS 26 arm64. To run it yourself: mise exec node@24.21.0 -- node explicit-this.mjs

describe uses this.name, but it is not a method of anything. call supplies the object for one call, followed by the arguments; apply supplies the object and then a single array of arguments, or any array-like object, one with a length and numbered keys, as the third line shows. The last line runs a method of shop with ravi as this, which prints Shop: Ravi: a method has no lasting tie to its object.

The this you pass is given to the function as it is, and then the function’s own mode decides what it becomes:

What call passes, as a sloppy and a strict function sees it JavaScript · this-coercion.cjs
// The this value a function receives from call(), in sloppy code (this CommonJS file) and in strict code.
function sloppy() {
  return this;
}
function strict() {
  'use strict';
  return this;
}

const show = (value) => {
  if (value === globalThis) return 'globalThis';
  if (value === null || value === undefined) return String(value);
  if (typeof value === 'object') return `${value.constructor.name} object`;
  return `${typeof value} ${String(value)}`;
};

console.log('call(...) with'.padEnd(16), 'sloppy gets'.padEnd(18), 'strict gets');
for (const [label, value] of [['undefined', undefined], ['null', null], ['5', 5], ["'chai'", 'chai'], ['{ id: 1 }', { id: 1 }]]) {
  console.log(label.padEnd(16), show(sloppy.call(value)).padEnd(18), show(strict.call(value)));
}

Output

call(...) with   sloppy gets        strict gets
undefined        globalThis         undefined
null             globalThis         null
5                Number object      number 5
'chai'           String object      string chai
{ id: 1 }        Object object      Object object

Recorded with Node.js 24.21.0 on macOS 26 arm64. To run it yourself: mise exec node@24.21.0 -- node this-coercion.cjs

A strict function, which includes every function in a module or a class, receives exactly what you passed: undefined, null or the number 5. A sloppy function replaces undefined and null with the global object and wraps a primitive in an object, so sloppy.call(5) sees a Number object. That is one more reason the output of the same code can differ between an old script and a module.

Borrowing methods

Many built-in methods, the array methods among them, are what the specification calls intentionally generic: they work on any object that looks right, so you can run them with call on values that do not have them. This used to be the standard way to treat array-likes as arrays:

Array methods on an array-like object and a string JavaScript · borrow-method.mjs
// Borrowing a method: running a method of one kind of object on a value that does not have it.
const drinks = { 0: 'chai', 1: 'coffee', 2: 'lassi', length: 3 }; // looks like an array, but is a plain object
console.log(Array.isArray(drinks), typeof drinks.map);
console.log(Array.prototype.map.call(drinks, (drink) => drink.toUpperCase()));
console.log(Array.prototype.join.call(drinks, ' / '));
console.log(Array.prototype.map.call('abc', (letter) => letter.charCodeAt(0))); // a string is array-like too

// Object.prototype.toString, borrowed, names the kind of any value, null and arrays included.
const kindOf = (value) => Object.prototype.toString.call(value);
console.log(kindOf([]), kindOf(null), kindOf(new Map()), kindOf(/x/));

// Modern code often has a direct way: Array.from turns an array-like object into an array.
console.log(Array.from(drinks, (drink) => drink.length));

Output

false undefined
[ 'CHAI', 'COFFEE', 'LASSI' ]
chai / coffee / lassi
[ 97, 98, 99 ]
[object Array] [object Null] [object Map] [object RegExp]
[ 4, 6, 5 ]

Recorded with Node.js 24.21.0 on macOS 26 arm64. To run it yourself: mise exec node@24.21.0 -- node borrow-method.mjs

  • drinks has numbered keys and a length but no map. Array.prototype.map.call(drinks, …) runs the array method with drinks as this, and the method only needs length and the numbered keys.
  • A string is array-like too, so map can walk its characters.
  • Object.prototype.toString.call(value) names the kind of any value, including null and arrays, which typeof reports as "object". Libraries used it for type checks before Array.isArray existed.

Today there is often a direct way: Array.from(drinks, fn) builds a real array and maps it in one step, and Object.hasOwn(obj, key) replaced the old borrowed Object.prototype.hasOwnProperty.call(obj, key). You will still read the borrowed forms in older code, so they are worth recognising.

apply and spread

Before spread syntax, apply was the only way to call a function with an array of arguments, so code such as Math.max.apply(null, readings) is everywhere in older libraries.

Math.max and a method, with apply, spread and Reflect.apply JavaScript · apply-vs-spread.mjs
// apply takes the arguments as an array. Spread syntax does the same in an ordinary call.
const readings = [31.5, 29.8, 33.1, 30.2];
console.log(Math.max.apply(null, readings), Math.max(...readings));

// When the function needs a this, spread keeps the object before the dot; apply takes it as its first argument.
const cart = {
  items: ['pen'],
  add(...more) {
    this.items.push(...more);
    return this.items.length;
  },
};
console.log(cart.add.apply(cart, ['ink', 'nib']));
console.log(cart.add(...['ruler']));
console.log(Reflect.apply(cart.add, cart, ['glue'])); // the same as apply, from the Reflect object
console.log(cart.items);

Output

33.1 33.1
3
4
5
[ 'pen', 'ink', 'nib', 'ruler', 'glue' ]

Recorded with Node.js 24.21.0 on macOS 26 arm64. To run it yourself: mise exec node@24.21.0 -- node apply-vs-spread.mjs

Math.max(...readings) gives the same 33.1 and reads better. When the function needs a this, spread keeps the object before the dot, cart.add(...more), while apply takes it as its first argument. Reflect.apply(fn, thisArg, args) is the same operation as a plain function, which some code prefers because it cannot be broken by an object that defines its own apply property. Both apply and spread turn every element into a separate argument, so both hit the engine’s limit on arguments for very large arrays; the lesson on parameters shows that limit.

bind: a function with this fixed

A bound function under call, bind, new and with arrows JavaScript · bind-rules.mjs
// bind returns a new function whose this, and optionally its first arguments, are fixed.
function describe(greeting, punctuation) {
  return `${greeting}, ${this.name}${punctuation}`;
}
const asha = { name: 'Asha' };
const ravi = { name: 'Ravi' };

const greetAsha = describe.bind(asha, 'Namaste'); // nothing runs yet
console.log(greetAsha('!'));
console.log(greetAsha.call(ravi, '?')); // a bound this cannot be replaced by call
console.log(greetAsha.bind(ravi)('.')); // or by binding again
console.log(`name: ${greetAsha.name} | length: ${greetAsha.length} (describe has ${describe.length})`);

// new ignores the bound this, but keeps the preset arguments.
function Point(x, y) {
  this.x = x;
  this.y = y;
}
const target = { ignored: true };
const PointOnYAxis = Point.bind(target, 0);
const p = new PointOnYAxis(5);
console.log(p, p instanceof Point, 'target.x:', target.x);

// An arrow function has no this to bind, but bind can still preset its arguments.
const thisOfArrow = () => this;
const add = (a, b) => a + b;
console.log(thisOfArrow.bind(asha)(), add.bind(null, 10)(5));

Output

Namaste, Asha!
Namaste, Asha?
Namaste, Asha.
name: bound describe | length: 1 (describe has 2)
Point { x: 0, y: 5 } true target.x: undefined
undefined 15

Recorded with Node.js 24.21.0 on macOS 26 arm64. To run it yourself: mise exec node@24.21.0 -- node bind-rules.mjs

call and apply run the function at once with the given this; bind returns a new function that does so when called.f.call(obj, a, b)f.apply(obj, [a, b])const g = f.bind(obj, a)runs f nowthis = objarguments a, bruns f nowthis = objarguments from the arrayreturns g,runs nothingg(b) laterthis = objarguments a, b

call and apply run the function now; bind returns a function for later

Text description of the diagram

The diagram has three rows, one per method.

  • `f.call(obj, a, b)` runs `f` at once, with `obj` as `this` and the arguments `a` and `b`, listed one by one.
  • `f.apply(obj, [a, b])` also runs `f` at once with `obj` as `this`, but takes the arguments from an array (or an array-like object).
  • `const g = f.bind(obj, a)` runs nothing. It returns a new function `g`. When `g(b)` is called later, `f` runs with `obj` as `this` and the arguments `a` and `b`: the preset argument first, then the new one.

describe.bind(asha, 'Namaste') returns a bound function, a new function object that remembers its target function, its this and its preset arguments. The output shows its rules:

  • The bound this wins. greetAsha.call(ravi, '?') still greets Asha, and binding the bound function again, greetAsha.bind(ravi), does not change it either. Later calls can only add arguments after the preset ones.
  • It looks like its target. Its name is "bound describe", and its length is the target’s length minus the preset arguments, here 2 minus 1. It has no prototype property of its own.
  • new ignores the bound this. new PointOnYAxis(5) builds a new Point: the preset argument 0 becomes x, 5 becomes y, and target, the bound this, is never touched. p instanceof Point is true.
  • Arrow functions cannot be given a this. thisOfArrow.bind(asha)() still returns undefined, the this of the module around it, although add.bind(null, 10) presets an argument as usual.

The most common use of bind is the one from the previous lesson: const addToCart = cart.add.bind(cart) makes one function you can pass to callbacks, timers and event listeners, and later remove again because it is always the same function. Calling bind again creates a different function each time, so removeEventListener(type, obj.fn.bind(obj)) removes nothing.

Partial application with bind

Presetting arguments is called partial application: a general function becomes a more specific one.

Two tax rates and a warning logger made with bind JavaScript · partial.mjs
// Partial application: bind fixes the first arguments and returns a function that takes the rest.
function priceWithTax(rate, amountInPaise) {
  return Math.round(amountInPaise * (1 + rate));
}

const withStandardRate = priceWithTax.bind(null, 0.18); // null: this function does not use this
const withReducedRate = priceWithTax.bind(null, 0.05);
console.log(withStandardRate(25000), withReducedRate(25000));
console.log([10000, 4999, 120].map(withStandardRate)); // map's extra arguments (index, array) are ignored

// The same with an arrow function: just as short, and it shows which argument is fixed.
const withStandardRateArrow = (amountInPaise) => priceWithTax(0.18, amountInPaise);
console.log(withStandardRateArrow(25000));

// A logger with a fixed first argument; console stays the this of log.
const warn = console.log.bind(console, '[warn]');
warn('stock is low:', 3);

Output

29500 26250
[ 11800, 5899, 142 ]
29500
[warn] stock is low: 3

Recorded with Node.js 24.21.0 on macOS 26 arm64. To run it yourself: mise exec node@24.21.0 -- node partial.mjs

priceWithTax.bind(null, 0.18) fixes the rate and returns a function that waits for the amount; null is the this because priceWithTax does not use one. The bound functions also work directly as map callbacks: map passes the value, then an index and the array, and priceWithTax ignores the extra ones. console.log.bind(console, '[warn]') makes a logger whose first argument is always the label.

An arrow function does the same job, (amount) => priceWithTax(0.18, amount), and many teams prefer it because the fixed argument is visible at the call site and the arrow’s this behaves like the surrounding code. Use bind when you need the bound function’s guarantees: a this that nobody can replace, or one stable function made from a method.

call, apply, bind and spread compared
Criterion callapplybindspread in a normal call
Runs the function NowNowLater, when the new function is calledNow
Arguments Listed one by oneOne array or array-likePreset ones now, the rest laterAny iterable, spread into the list
Sets this Yes, for this callYes, for this callYes, for every callOnly through obj.method(...args)
Returns The function's resultThe function's resultA new bound functionThe function's result
When to choose Borrowing a method for one callOlder code with arguments already in an arrayCallbacks and listeners that need a fixed thisCalling with an array of arguments today
JavaScript & TypeScript Online Compiler Run bind-rules.mjs, then bind describe twice with different preset arguments and predict the output.

Exercise: write bind yourself

Exercise · Medium · JavaScript

Write bind yourself, with preset arguments and new

Write myBind(fn, thisArg, ...preset) in my-bind.mjs so that it behaves like fn.bind(thisArg, ...preset), without using bind. The function it returns:

  • calls fn with thisArg as this, and with the preset arguments followed by its own arguments, and returns what fn returns: myBind(describe, asha, 'Hi')('!') is describe.call(asha, 'Hi', '!');
  • keeps thisArg even when it is called through call or apply with another this;
  • when it is called with new, ignores thisArg, still passes the preset arguments first, and returns a new instance of fn.

The tests replace Function.prototype.bind with a function that throws, so a solution that calls bind fails.

Starter code · my-bind.mjs

// myBind(fn, thisArg, ...preset): what fn.bind(thisArg, ...preset) returns, written without bind (the exercise of
// lesson javascript/call-apply-and-bind). This version ignores thisArg and new.

export function myBind(fn, thisArg, ...preset) {
  return function (...args) {
    return fn(...preset, ...args);
  };
}
The sample tests · my-bind.test.mjs
import { test, assert } from 'mysmartcopilot:test';
import { myBind } from './my-bind.mjs';

function describe(greeting, punctuation) {
  return `${greeting}, ${this.name}${punctuation}`;
}
const asha = { name: 'Asha' };
const ravi = { name: 'Ravi' };

test('fixes this', () => {
  const nameOf = myBind(function () {
    return this.name;
  }, asha);
  assert.equal(nameOf(), 'Asha');
});

test('passes the preset arguments before the call arguments', () => {
  const join = (...parts) => parts.join('-');
  assert.equal(myBind(join, null, 'a')('b', 'c'), 'a-b-c');
  assert.equal(myBind(join, null, 'a', 'b')('c'), 'a-b-c');
  assert.equal(myBind(join, null)(), '');
});

test('returns what the function returns, with this and arguments together', () => {
  const greet = myBind(describe, asha, 'Hi');
  assert.equal(greet('!'), 'Hi, Asha!');
});

test('call and apply cannot replace the bound this', () => {
  const greet = myBind(describe, asha, 'Hi');
  assert.equal(greet.call(ravi, '?'), 'Hi, Asha?');
  assert.equal(greet.apply(ravi, ['.']), 'Hi, Asha.');
});

test('new ignores thisArg and keeps the preset arguments', () => {
  function Point(x, y) {
    this.x = x;
    this.y = y;
  }
  const target = {};
  const OnYAxis = myBind(Point, target, 0);
  const p = new OnYAxis(5);
  assert.ok(p instanceof Point, 'the result should be a Point');
  assert.equal(p.x, 0);
  assert.equal(p.y, 5);
  assert.deepEqual(target, {}, 'thisArg should not be touched');
});

test('does not use Function.prototype.bind', () => {
  const original = Function.prototype.bind;
  Function.prototype.bind = function () {
    throw new Error('bind was used');
  };
  try {
    const add = myBind(function (a) {
      return this.base + a;
    }, { base: 40 });
    assert.equal(add(2), 42);
  } finally {
    Function.prototype.bind = original;
  }
});
A hint

Return a regular function, not an arrow: it needs new.target, which is undefined in a normal call and set when the function is called with new. In a normal call, fn.apply(thisArg, [...preset, ...args]) runs fn with the right this and arguments. With new, build the object with new fn(...preset, ...args) and return it.

The sample tests run on this device, in your browser (QuickJS): nothing is sent to mysmartcopilot.com. The first run downloads JavaScript (about 0.6 MB), which is kept for the next runs. A check in your browser is feedback for you, not proof that the code is right for every input.

Self-check

Check yourself

6 questions about this lesson. Every answer and why it is right is on the page, behind “Show the answer”. Your score stays in this browser.

  1. Question 1 of 6 What does this module print?

    Read the code, then choose one answer.

    function f(a, b) {
      return [this.x, a, b];
    }
    const g = f.bind({ x: 1 }, 2);
    
    console.log(g.call({ x: 9 }, 3));
    Show the answer to question 1

    Answer: [ 1, 2, 3 ]

    bind fixed both this ({ x: 1 }) and the first argument (2). call can only add arguments after the preset ones; the this it passes is ignored by a bound function.

  2. Question 2 of 6 const later = report.bind(stats); What has happened once this line has run?

    Choose one answer.

    Show the answer to question 2

    Answer: Nothing has run: later is a new function that calls report with stats as this when it is called

    bind returns a new bound function and leaves report unchanged. call and apply are the ones that run the function at once.

  3. Question 3 of 6 What does this module print?

    Read the code, then choose one answer.

    function Box(value) {
      this.value = value;
    }
    const PresetBox = Box.bind({ value: 'ignored' }, 'kept');
    
    console.log(new PresetBox().value);
    Show the answer to question 3

    Answer: 'kept'

    Calling a bound function with new ignores the bound this: a new object is created, as with new Box. The preset arguments are still passed, so value is 'kept'.

  4. Question 4 of 6 What does this module print?

    Read the code, then choose one answer.

    const whoAmI = () => this;
    
    console.log(whoAmI.call({ name: 'Asha' }));
    Show the answer to question 4

    Answer: undefined

    An arrow function has no this of its own, so call, apply and bind cannot give it one. It uses the this of the code around it, and at the top level of a module that is undefined.

  5. Question 5 of 6 What does this module print?

    Read the code, then choose one answer.

    function area(width, height) {
      return width * height;
    }
    const ofWidth2 = area.bind(null, 2);
    
    console.log(ofWidth2.name, ofWidth2.length, ofWidth2(5));
    Show the answer to question 5

    Answer: bound area 1 10

    A bound function is named "bound " followed by the original name, and its length is the original length minus the preset arguments, here 2 minus 1. Calling it with 5 runs area(2, 5).

  6. Question 6 of 6 const once = f.bind({ id: 1 }); const twice = once.bind({ id: 2 }); If f returns this, what is twice().id?

    Choose one answer.

    Show the answer to question 6

    Answer: 1

    Binding a bound function again is allowed, but the first this stays: twice calls once, and once ignores the this it receives. A second bind can only add more preset arguments.

Interview questions

Fresher · What is the difference between call, apply and bind?

All three let you choose the this of a function. call runs the function immediately with the this you give and the arguments listed one by one, as in greet.call(user, 'Hi', '!'). apply also runs it immediately but takes the arguments as one array, greet.apply(user, ['Hi', '!']); spread syntax has replaced most uses of it. bind runs nothing: it returns a new function whose this, and optionally its first arguments, are fixed, which is useful for callbacks and event listeners. A bound function’s this cannot be changed by a later call, apply or bind, but calling it with new ignores the bound this and creates a new object. None of the three can change the this of an arrow function, which always uses the this of the code around it.

Model answers open with any pass: ₹79 for 1 week ($2.99), one payment that never renews.

  1. Mid to senior

    A curry(fn) helper collects arguments until it has fn.length of them, using bind to remember the ones so far. It works for (a, b, c) => ..., but curry((amount, rate = 0.18) => ...) returns a number after one argument, and curry((...parts) => ...) never waits at all. Explain both, and fix the design.

    Model answer, Pro. Model answers open with any pass; prices are in the notice on this page. First step and follow-ups

  2. Fresher to mid

    A debounce helper is (fn, ms) => { let t; return () => { clearTimeout(t); t = setTimeout(() => fn(), ms); }; }. Used on a method, editor.save = debounce(editor.save, 300), it never saves and the event argument is lost. Explain both bugs and write a version that keeps this and the arguments.

    Model answer, Pro. First step and follow-ups

Key takeaways

  • f.call(thisArg, a, b) and f.apply(thisArg, [a, b]) run f once with the this you choose; f.bind(thisArg, a) returns a new function and runs nothing.
  • A strict function receives the this you pass as it is; a sloppy one turns undefined and null into the global object and wraps primitives.
  • Borrowing, such as Array.prototype.map.call(arrayLike, fn), runs a method on a value that lacks it; Array.from and Object.hasOwn replace the most common borrowed forms.
  • A bound function’s this cannot be replaced by call, apply or another bind; new ignores it but keeps the preset arguments.
  • Arrow functions ignore the this of call, apply and bind; spread syntax has replaced most uses of apply.

References

Related tools

Report a problem with this lesson

Quick answers and tool search

Type to search tools or to get a quick answer, for example 18% of 2500. Use the up and down arrow keys to move through the results, Enter to choose, and Escape to close.