this is a keyword that refers to an object. Which object depends on how the function is called, not where it was written. That one rule explains nearly every this surprise.
When you call a method with a dot, this is the object to the left of the dot:
const student = {
first: "Maya",
last: "Ortiz",
fullName() {
return this.first + " " + this.last;
}
};
console.log(student.fullName()); // this = student
You could write student.first inside the method, but then the method only ever works for that one variable. With this, the same method works for whichever object calls it:
function describe() {
return `${this.name} (${this.type})`;
}
const a = { name: "Rex", type: "dog", describe };
const b = { name: "Tweety", type: "bird", describe };
console.log(a.describe());
console.log(b.describe());
One function, shared by two objects. this changes depending on which one is to the left of the dot. This idea is what makes constructors possible (the last lesson in this section).
If you pull a method out of its object and call it on its own, there is no dot and no object to the left. this is lost:
const counter = {
count: 5,
show() { return this.count; }
};
console.log(counter.show()); // 5
const loose = counter.show; // copy the function out
console.log(loose()); // undefined (or an error in strict mode)
This happens most often when handing a method to something else to call later:
const clock = {
time: "12:00",
ring() { console.log("Ding at " + this.time); }
};
setTimeout(clock.ring, 1000); // "Ding at undefined" (this is lost)
setTimeout(() => clock.ring(), 1000); // "Ding at 12:00" (called with a dot)
obj.method as a callback passes only the function, not the object. Wrap it in an arrow function, () => obj.method(), so it is still called with the dot.
bind makes a copy of a function with this permanently set:
const locked = counter.show.bind(counter);
console.log(locked()); // 5 (works no matter how it is called)
Arrow functions do not have their own this. They use whatever this was in the code around them. That is bad for methods, but useful for callbacks inside methods:
const timer = {
label: "Oven",
start() {
// arrow callback: uses start()'s this, which is timer
setTimeout(() => {
console.log(this.label + " done");
}, 1000);
}
};
timer.start();
With a regular function callback instead of the arrow, this inside would not be timer, and you would get undefined done.
Outside any object, this still has a value. Used alone in a script, it refers to the global object, which in a browser is window (the object holding the whole browser tab). Inside a plain function called without a dot, it is the global object too, unless strict mode is on:
console.log(String(this)); // used alone
function myFunction() {
return this;
}
console.log(String(myFunction())); // plain function
function strictFunction() {
"use strict";
return this;
}
console.log(strictFunction());
This is the reason loose() above gave undefined: this was window, which has no count property. In strict mode, this would be undefined and this.count would throw a TypeError instead, which is at least easier to spot.
| How it is called | this is... |
|---|---|
| obj.method() | obj |
| fn() (no dot) | undefined in strict mode; the global object otherwise |
| new Fn() | the brand-new object being built |
| fn.bind(obj)() | obj, always |
| arrow function | same as the surrounding code |
| event listener (regular fn) | the element that received the event |
this is not what you expect, look at the line where the function is called, not where it is written. Is there an object and a dot right before it?
What does this log?
function hi() { return "Hi, " + this.name; }
const a = { name: "Ana", hi };
const b = { name: "Ben", hi };
console.log(b.hi());
Hi, Ben. this is whatever is to the left of the dot at the call: b.setTimeout(clock.ring, 1000) logs "Ding at undefined". Which fix works?
setTimeout(clock.ring(), 1000)setTimeout(() => clock.ring(), 1000)setTimeout(this.ring, 1000)setTimeout("clock.ring", 1000)ring with clock. in front, so this is the clock. A runs it immediately. C and D do not refer to the clock's method correctly. (clock.ring.bind(clock) would also work.)To find out what this will be, where should you look?
this is decided by how the function is called. (Arrow functions are the exception: they use the this of the code they were written in.)rectangle an area() method using this, and log the resultdescribe() function between two different objectsthis disappear. Fix it with bindsetTimeout with an arrow callback that reads this