No keyword in the language causes as much confusion. The reason is simple: in most languages this is fixed when the code is written, whereas in JavaScript it is decided when the function is called.
The same function can therefore see several different values depending on how it is triggered.
Definition
this means the object a function is currently being called on. It is not the object the function is written inside, it is the one sitting to the left of the dot at the moment of the call.
const counter = {
total: 0,
increment() {
this.total += 1;
return this.total;
},
};
console.log(counter.increment()); // 1
console.log(counter.increment()); // 2Here counter sits to the left of the dot, so this means that object, and the increment really does land on it.
Five calling forms, five results
The table below reads as a grid: find the shape of the call, read the value you get.
| Shape of the call | Value of this |
|---|---|
| object.method() | The object placed before the dot |
| fn() inside a module | undefined |
| new Model() | The instance being built |
| fn.call(target) | The target passed as first argument |
| Arrow function | The one from the surrounding code |
The second row deserves a note: inside a module or in strict mode, a call with no object gives undefined. In an old script loaded outside module mode, the same line gives the global object.
The detached method trap
Storing a method in a variable, or passing it as an argument, cuts the link with its object. This is the most frequent source of errors around the keyword.
const counter = {
total: 0,
increment() { this.total += 1; return this.total; },
};
const detached = counter.increment;
try {
detached(); // nothing left of the dot any more
} catch (error) {
console.log(error.name); // TypeError
}
const bound = counter.increment.bind(counter);
console.log(bound()); // 1The bind method builds a new function whose value is frozen for good. The other solution, often more readable, is to wrap the call in an arrow function: an arrow has no value of its own and reaches for the one in the surrounding code.
const timer = {
seconds: 0,
start() {
setTimeout(() => {
this.seconds += 1;
console.log(this.seconds); // 1
}, 0);
},
};
timer.start();With a classic function in place of the arrow, the addition would produce NaN: setTimeout is what calls the function, with no object to the left of any dot.
Frequently asked questions
Why does an arrow function have no this of its own?
Because it never receives one at call time: it uses the value from the code it is written in, exactly like any other variable of that code. That makes it ideal inside a method, and unsuitable for defining a method itself, where it would reach for a value that does not exist.
What is the difference between call, apply and bind?
call and apply invoke the function immediately while forcing the value, the only difference being how arguments are passed, one by one or inside an array. bind invokes nothing: it returns a new function with the value frozen, to trigger later.
How do I work out what this means in code I am reading?
Do not look at the function declaration, look for the call. The question is always the same: what sits to the left of the dot at the moment the function is triggered. Our JavaScript course spends real time on this mechanism, because it governs how you read any object-oriented code.