On this page

this, call, apply & bind

3 min read

Closures decide which variables a function can see. this decides which object a function is working on. It is the most confusing part of JavaScript, and it caused the famous "lost this" bug in React class components.

Definition

this is a special value that is set when a function is called, not when it is written. It usually means "the object before the dot".

Analogy: the word "I". It means a different person depending on who is speaking. In the same way, this means a different object depending on how the function is called.

The Four Rules

JavaScript decides this using these rules, from strongest to weakest.

RuleHow it is calledthis is
newnew Person()The new object being created
Explicitfn.call(obj), apply, bindThe obj you passed
Implicitobj.fn()obj (the object before the dot)
Defaultfn()undefined in strict mode, window otherwise
js
1const user = {
2 name: "Asha",
3 hi() { console.log(this.name); },
4};
5
6user.hi(); // "Asha" (implicit: object before the dot)
7
8const loose = user.hi;
9loose(); // undefined ✗ (default rule, the dot is gone)

Here is what breaks. Pulling a method out of its object loses this. This is exactly what happens when you pass a method as a callback.

Arrow Functions

Arrow functions have no this of their own. They use the this from the place where they were written (lexical, just like closures).

js
1const timer = {
2 seconds: 0,
3 start() {
4 setInterval(() => {
5 this.seconds++; // ✓ this is timer, taken from start()
6 }, 1000);
7 },
8};

With a normal function () {} inside setInterval, this would be lost. Because of this, arrows are the usual choice for callbacks. Do not use arrows for object methods or constructors, and call, apply and bind cannot change an arrow's this.

call, apply and bind

All three let you choose this yourself.

js
1function intro(greeting, punct) {
2 return greeting + ", " + this.name + punct;
3}
4const asha = { name: "Asha" };
5
6intro.call(asha, "Hi", "!"); // "Hi, Asha!" arguments one by one
7intro.apply(asha, ["Hi", "!"]); // "Hi, Asha!" arguments as an array
8const bound = intro.bind(asha, "Hi"); // returns a NEW function, does not run it
9bound("?"); // "Hi, Asha?"
Runs now?ArgumentsReturns
callYesListedFunction result
applyYesArrayFunction result
bindNoListed (can be partial)A new function

A mnemonic: Apply takes an Array. Call takes commas.

In React

Class components needed bind because event handlers lose this:

jsx
1class Button extends React.Component {
2 constructor(props) {
3 super(props);
4 this.handleClick = this.handleClick.bind(this); // ✓ fix lost this
5 }
6 handleClick() { this.setState({ clicked: true }); }
7 render() { return <button onClick={this.handleClick}>Go</button>; }
8}

Without the bind, React calls handleClick as a plain function, this is undefined, and this.setState crashes. Another fix was an arrow property: handleClick = () => { ... }.

Function components do not have this problem. They have no this at all. State and props are plain variables captured by closures. That is one of the biggest reasons the ecosystem moved to function components and hooks.

Summary

1this → decided at call time, not at write time
2Rules → new, explicit (call/apply/bind), implicit (obj.fn()), default
3Lost this → happens when a method is detached from its object
4Arrow functions → no own this, use the surrounding one
5call → run now, args listed apply → run now, args in array bind → returns new function
6React classes → bind in constructor or use arrow properties
7Function components → no this, closures instead

Next we move from how functions behave to how JavaScript schedules them. That is the event loop.

Next TopicEvent Loop: Micro vs Macrotasks