Encodera

Thanks for reading!

Donate to the site — we work hard for you.

Contents

this, call, apply and bind

Inside a method, the this keyword points to the object the method was called on. An important detail: this is determined at the moment of the call, not when the function was written. So a method passed somewhere separately from its object can "lose" its this. The call, apply and bind methods let you set it explicitly.

this depends on the call

When a function is called through a dot — user.greet() — this inside it equals the object to the left of the dot:

const user = {
  name: 'Anna',
  greet() {
    return `Hi, I am ${this.name}`;
  },
};
 
console.log(user.greet()); // Hi, I am Anna

Losing this

If you store a method in a variable and call it without the object, the link to the object is gone. In classes this then equals undefined, and reading a property from it throws an error:

class User {
  constructor(name) {
    this.name = name;
  }
 
  greet() {
    return `Hi, I am ${this.name}`;
  }
}
 
const anna = new User('Anna');
const greet = anna.greet;
 
try {
  greet();
} catch (error) {
  console.log(error.name); // TypeError
}

call

The call() method calls a function right away and sets its this — the first argument. The remaining arguments are passed to the function as usual:

function introduce(city) {
  return `${this.name} from ${city}`;
}
 
const oleg = { name: 'Oleg' };
 
console.log(introduce.call(oleg, 'Riga')); // Oleg from Riga

This way one function can be used with different objects.

apply

apply() does the same, but takes the function's arguments as an array:

function introduce(city, country) {
  return `${this.name}: ${city}, ${country}`;
}
 
const maria = { name: 'Maria' };
const place = ['Madrid', 'Spain'];
 
console.log(introduce.apply(maria, place)); // Maria: Madrid, Spain

Today apply() is less common: people write introduce.call(maria, ...place) with the spread operator instead.

bind

bind() does not call the function; it returns a new one whose this is fixed for good. You can pass it anywhere:

const user = {
  name: 'Anna',
  greet() {
    return `Hi, I am ${this.name}`;
  },
};
 
const greet = user.greet.bind(user);
 
console.log(greet()); // Hi, I am Anna

bind() can also fix the first arguments. If this is not needed, pass null:

const multiply = (a, b) => a * b;
const double = multiply.bind(null, 2);
 
console.log(double(5)); // 10

Arrow functions

An arrow function has no this of its own: it takes it from the surrounding code. So inside a method an arrow sees the same object, and call and bind have no effect on it:

const counter = {
  count: 0,
  addAll(numbers) {
    numbers.forEach(() => {
      this.count++;
    });
    return this.count;
  },
};
 
console.log(counter.addAll([1, 2, 3])); // 3

Common mistake

Passing a method as a callback — to a timer or an event handler. setTimeout(timer.tick, 100) passes the function without the object, and this is lost inside tick. Fix it with bind() or wrap the call in an arrow:

class Timer {
  constructor() {
    this.seconds = 0;
  }
 
  tick() {
    this.seconds++;
    console.log(this.seconds);
  }
}
 
const timer = new Timer();
 
setTimeout(timer.tick.bind(timer), 100); // 1
setTimeout(() => timer.tick(), 200); // 2

Thanks for reading!

Donate to the site — we work hard for you.