When Bitsy says "me", it means Bitsy. When Bob says "me", it means Bob. Same word, different person. The this keyword works the same way, so one function can be shared by many objects and still know which one is using it.
this is the object before the dot
A method is a function that lives inside an object. Inside a method, this is the object before the dot when you call it.
const robot = {
name: "Bitsy",
greet() {
return this.name;
},
};
console.log(robot.greet()); // prints: Bitsy
You called robot.greet(). The object before the dot is robot, so this is robot, and this.name is "Bitsy".
One function, many objects
This is the reason this changes. You can write a function once and share it between objects. Each time it runs, this points at whichever object called it.
function greet() {
return "Hi, I'm " + this.name;
}
const bitsy = { name: "Bitsy", greet };
const bob = { name: "Bob", greet };
console.log(bitsy.greet()); // prints: Hi, I'm Bitsy
console.log(bob.greet()); // prints: Hi, I'm Bob
Same function, same word, different object. So this depends on how you call the function, not on where you wrote it.
The trap: losing this
Take the function out of the object and call it on its own. Now there is nothing before the dot, so this is lost.
const robot = {
name: "Bitsy",
greet() {
"use strict";
return this.name;
},
};
const greet = robot.greet;
greet();
The "use strict" line turns on strict mode, the safer setting most modern code uses. Code inside classes and JavaScript modules is always strict. In strict mode, this is undefined here, so this.name throws TypeError: Cannot read properties of undefined (reading 'name'). Without strict mode, this falls back to the global object, and you quietly get the wrong name, usually undefined or an empty string.
This happens more often than you'd think. Passing a method somewhere else, like setTimeout(robot.greet, 1000), hands over the bare function, and the dot is gone by the time it gets called.
The twist: bind and arrow functions
You can glue this in place with bind. It gives you a new function where this is always the object you chose.
const robot = {
name: "Bitsy",
greet() {
return this.name;
},
};
const greet = robot.greet.bind(robot);
console.log(greet()); // prints: Bitsy
Now greet always knows it belongs to robot, however you call it.
Arrow functions are different. They don't have their own this at all. They use the this of the code around them. That makes them a good fit for callbacks inside a method.
const robot = {
name: "Bitsy",
friends: ["Bob", "Mia"],
greetAll() {
return this.friends.map(f => this.name + " waves at " + f);
},
};
console.log(robot.greetAll()); // prints: [ 'Bitsy waves at Bob', 'Bitsy waves at Mia' ]
The arrow function borrows this from greetAll, which was called as robot.greetAll(), so this is robot.
A quick checklist
When you're not sure what this is, ask how the function was called.
- Called with a dot, like
robot.greet():thisis the object before the dot. - Made with
bind:thisis the object you passed tobind, every time. - An arrow function:
thisis whatever it is in the code around the arrow. - Called on its own, like
greet():thisisundefinedin strict mode, or the global object otherwise.
Most bugs with this come from the last case, a method that lost its dot along the way.
Rule of thumb
For regular functions, look at what is before the dot when you call it, and that is this.
Writing a method as an arrow function, like greet: () => this.name. The arrow has no this of its own, so it does not point at the object. Use the method shorthand greet() { ... } for methods.
Check yourself
const robot = { name: "Bitsy", greet() { return this.name; } }, what is this when you call robot.greet()?For a regular function called as a method, this is the object before the dot at call time, which here is robot.