Newsletter

JavaScript bind(), call() and apply(): understand and control this

Why this changes value depending on how a function is called, and how call(), apply() and bind() pin it down: worked examples, the callback and class cases, comparison with arrow functions.

JavaScript bind(), call() and apply(): how they differ
Quick answer

call() and apply() run a function immediately with a this of your choosing; call takes the arguments one by one, apply takes them in an array. bind() runs nothing: it returns a new function whose this (and optionally its first arguments) are fixed for good. You use it when a method is passed as a callback and loses its object.

Every JavaScript function has three methods, call(), apply() and bind(), that serve the same purpose: deciding what this is when the function runs. To understand what they are for, you first need to understand the problem they solve: in JavaScript, this does not depend on where a function is written, but on how it is called.

The problem: this changes with the call

Four rules are enough to predict this in almost every case:

  1. Method call objet.fonction(): this is objet.
  2. Plain call fonction(): this is undefined in strict mode (modules, classes), and the global object window in non-strict mode.
  3. Call with new: this is the object under construction.
  4. Call through call, apply or a function bound with bind: this is whatever you decided.

Arrow functions escape these rules: they have no this of their own and keep the one from the surrounding code.

The case that hurts in practice is the slide from rule 1 to rule 2:

probleme.js
const person = {
  name: 'Sarah',
  hello() {
    return `Bonjour ${this.name}`;
  },
};

person.hello();            // 'Bonjour Sarah'  : method call, this = person

const detached = person.hello;
detached();                // TypeError in strict mode: this is undefined
                           // ('Bonjour undefined' or 'Bonjour ' in non-strict mode)

The function has not changed. Only the way it is called has, and this with it. That is exactly what happens when you pass a method to setTimeout, addEventListener or map.

call(): calling with a this of your choice

fonction.call(thisArg, arg1, arg2, …) runs the function immediately, with this equal to thisArg and the arguments passed one by one.

call.js
function presenter(age, ville) {
  return `${this.name}, ${age} ans, ${ville}`;
}

const person = { name: 'Sarah' };

presenter.call(person, 30, 'Lille');           // 'Sarah, 30 ans, Lille'
presenter.call({ name: 'Léa' }, 25, 'Paris');   // 'Léa, 25 ans, Paris'

const detached = person.hello;                  // the method from the previous example
detached.call(person);                          // 'Bonjour Sarah' : this is restored

A classic use: reusing an array method on something that is not an array.

javascript
Array.prototype.slice.call('abc');   // ['a', 'b', 'c']
// Nowadays you would write:
Array.from('abc');                   // ['a', 'b', 'c']

apply(): the same thing, with the arguments in an array

fonction.apply(thisArg, [arg1, arg2, …]) does exactly what call does, but receives the arguments as an array. Handy when they already come grouped.

apply.js
presenter.apply(person, [30, 'Lille']);   // 'Sarah, 30 ans, Lille'

const notes = [3, 9, 4];
Math.max.apply(null, notes);   // 9 : Math.max expects separate arguments, not an array

Since ES2015, the spread operator has made apply rarely necessary: Math.max(...notes) and presenter.call(person, ...args) do the same job while staying readable. You mostly come across apply in code written before then.

bind(): fixing this for later

Where call and apply run straight away, bind runs nothing. It returns a new function whose this is fixed for good. It is the tool for callbacks: you prepare the function now, and someone else will call it later.

bind.js
const bound = person.hello.bind(person);

bound();                          // 'Bonjour Sarah', whatever the calling context
bound.call({ name: 'X' });        // 'Bonjour Sarah' : bind wins, call can no longer change this
bound.name;                       // 'bound hello'

The typical case: a method passed as a callback

compteur.js
class Compteur {
  constructor() {
    this.n = 0;
  }
  incrementer() {
    this.n += 1;
    return this.n;
  }
}

const compteur = new Compteur();

// Without bind: the method is detached from its object
const inc = compteur.incrementer;
inc();   // TypeError: Cannot read properties of undefined (reading 'n')

// With bind: this is fixed to compteur
const incLie = compteur.incrementer.bind(compteur);
incLie();   // 1
incLie();   // 2

The same pattern applies to timers and browser events:

javascript
setTimeout(compteur.incrementer.bind(compteur), 1000);
bouton.addEventListener('click', compteur.incrementer.bind(compteur));

// To be able to remove the listener later, keep the bound reference:
const onClick = compteur.incrementer.bind(compteur);
bouton.addEventListener('click', onClick);
bouton.removeEventListener('click', onClick);

Without bind, addEventListener would call incrementer with this set to the clicked button: this.n would be undefined and the counter would count nothing.

bind() for partial application

The arguments passed to bind after this are fixed as well. The returned function expects the remaining ones.

partiel.js
const presenterSarah = presenter.bind(person, 30);
presenterSarah('Lille');   // 'Sarah, 30 ans, Lille' : only the last argument is left to supply

const add = (a, b) => a + b;
const add5 = add.bind(null, 5);   // this is not used: null will do
add5(3);                          // 8

It is a simple way to specialise a generic function without writing a wrapper function.

Arrow functions: when bind becomes unnecessary

An arrow function captures the this of the place where it is written and ignores it from then on. Two consequences.

First, call, apply and bind cannot change its this:

javascript
const arrow = () => this;
arrow.call({ a: 1 });   // the module's this, not { a: 1 }

Second, and this is the everyday use, an arrow function declared inside a method or a class keeps the right this without bind:

fleche.js
class Compteur {
  n = 0;

  // Arrow-function property: this is captured once and for all
  incrementer = () => {
    this.n += 1;
    return this.n;
  };
}

const c = new Compteur();
const inc = c.incrementer;
inc();   // 1 : works without bind

setTimeout(() => c.incrementer(), 1000);   // or an arrow function at the call site

The flip side: an arrow-function property is recreated for every instance, whereas a regular method is shared through the prototype. For a handful of objects it makes no difference; for thousands of instances, a method bound once in the constructor (this.incrementer = this.incrementer.bind(this)) saves memory.

this passed as a second argument

Several array methods accept the this value to use in the callback directly, which saves a bind:

javascript
const config = { k: 10 };
[1, 2, 3].map(function (x) { return this.k * x; }, config);   // [10, 20, 30]

forEach, filter, some and every take the same second argument. With an arrow function, it becomes unnecessary.

Comparison table

call apply bind
Runs the function immediately immediately no: returns a function
Arguments one by one in an array one by one, fixed for future calls
Typical use borrowing a method spreading an array (replaced by ...) callbacks, events, partial application
Effect on an arrow function none on this none on this none on this, the arguments are fixed

Common errors and how to read them

  • “Cannot read properties of undefined (reading ‘n’)” in a class method: the method has been detached from its instance (rule 2). Bind it with bind or declare it as an arrow-function property.
  • An event listener that does not update the state: this is the DOM element, not your object. Same remedy.
  • A bind that seems to do nothing: the function is an arrow function. Remove the bind, it is pointless.
  • removeEventListener that removes nothing: every bind creates a new function; you have to pass the same bound reference when adding and when removing.

To go further, classes in JavaScript come back to methods and the prototype, and the Functions chapter of the tutorial lays the groundwork if the vocabulary in this article is still new. In PHP, the question arises differently: $this never changes object.

Common errors

Passing a method as a callback without bind bouton.addEventListener('click', compteur.incrementer) calls the function with this set to the button, not to compteur. Write compteur.incrementer.bind(compteur) or use an arrow function.
Expecting bind on an arrow function to change this An arrow function has no this of its own: call, apply and bind leave it untouched. Only the arguments passed through bind are taken into account.
Calling bind on every render bind creates a new function on every call. In a loop or a repeated render, bind once in the constructor (or declare the method as an arrow-function property).
Mixing up call and apply with the same argument list f.call(obj, [1, 2]) passes a single argument, the array. To spread an array, use apply, or f.call(obj, ...tableau).

JavaScript

Damien Flandrin Web developer since 2010, creator of Gekkode and Email Impact. Every article is tested on a real project before publication. Contact
Newsletter

New tests, tutorials and projects, by e-mail.

Reproducible tests, versioned code, dated results. Never any spam.