The this Keyword
Objectives
By the end of this chapter, you should be able to:
- Explain that
thisdepends on how a function is called, not where it’s written - Predict the value of
thisin a method call, a plain function call, and an arrow function - Explain why a constructor call sets
thisto the new object being built - Use
.call(),.apply(), and.bind()to setthisexplicitly
💡 Why this matters: Every lesson left in this module leans on
this, constructors, methods, inheritance, all of it.thistrips up more beginners than almost anything else in JavaScript, because its value isn’t fixed. Get comfortable with it now, before adding classes on top.
this Depends on How a Function Is Called
In most languages, a variable’s value depends on where it’s declared. this doesn’t work that way. Its value is decided at the moment a function is called, based on how you called it, not where the function was written.
That’s the one idea this whole lesson is really about. Everything below is just that idea applied to four different situations.
Method Calls: this Is the Object Before the Dot
Call a function as a property of an object, and this inside it refers to that object.
"use strict";
const priya = {
name: "Priya",
greet() {
return `Hi, I'm ${this.name}`;
}
};
console.log(priya.greet());
// Hi, I'm Priya
priya.greet() has priya right before the dot, so this is priya. That’s it: whatever object appears before the dot when you call a method is what this refers to inside it.
Plain Function Calls: this Is Undefined
Pull that same function out of the object and call it on its own, with nothing before the dot, and this loses its connection entirely.
"use strict";
const priya = {
name: "Priya",
greet() {
return `Hi, I'm ${this.name}`;
}
};
const greetFn = priya.greet;
try {
console.log(greetFn());
} catch (error) {
console.log(error.constructor.name + ": " + error.message);
}
// TypeError: Cannot read properties of undefined (reading 'name')
greetFn() is called with nothing before the dot, so in strict mode (which this course uses throughout, see Module 2), this is undefined. Reading this.name when this is undefined throws. This is exactly why priya.greet() worked but greetFn() didn’t: same function, different call, different this.
Arrow Functions Inherit this From Their Surroundings
Arrow functions don’t get their own this at all. Instead, they use whatever this was already in scope where the arrow function was written. This matters a lot inside a regular function that contains another function.
"use strict";
const team = {
name: "Rockets",
membersRegular: ["Erin", "Jordan"],
listRegular: function () {
this.membersRegular.forEach(function (member) {
console.log(this);
});
},
membersArrow: ["Erin", "Jordan"],
listArrow: function () {
this.membersArrow.forEach((member) => {
console.log(`${this.name}: ${member}`);
});
}
};
team.listRegular();
// undefined
// undefined
team.listArrow();
// Rockets: Erin
// Rockets: Jordan
listRegular calls .forEach() with a regular function (member) {...} callback. .forEach() calls that callback as a plain function, the same situation as the last section, so this inside it is undefined, not team. listArrow uses an arrow function instead, which has no this of its own, so it uses listArrow’s this, which is team. That’s why only the arrow version can reach this.name.
This is the practical reason to reach for an arrow function inside a method: when you want the inner function to keep using the surrounding this, not lose it.
Constructor Calls: this Is the New Object
Call a function with new, and JavaScript creates a brand new object, then runs the function with this set to that new object.
"use strict";
function Dog(name) {
this.name = name;
}
Dog.prototype.bark = function () {
return `${this.name} says woof!`;
};
const rex = new Dog("Rex");
console.log(rex.bark());
// Rex says woof!
Inside Dog, this.name = name assigns to the new object new just created, which is why rex.name ends up set to "Rex". You’ll see this exact behavior again in the next lesson, class constructors work the same way.
Setting this Explicitly: call, apply, and bind
Sometimes you want to choose this yourself, instead of relying on how a function happens to be called. .call(), .apply(), and .bind() all let you do that.
"use strict";
function introduce(greeting) {
return `${greeting}, I'm ${this.name}`;
}
const maya = { name: "Maya" };
console.log(introduce.call(maya, "Hi"));
// Hi, I'm Maya
console.log(introduce.apply(maya, ["Hello"]));
// Hello, I'm Maya
const boundIntroduce = introduce.bind(maya);
console.log(boundIntroduce("Hey"));
// Hey, I'm Maya
.call(thisValue, arg1, arg2, ...) and .apply(thisValue, [arg1, arg2]) both run the function immediately with this set to whatever you pass in, they only differ in how you pass the remaining arguments (individually for .call(), as an array for .apply()). .bind(thisValue) is different: it doesn’t run the function, it returns a new function with this permanently locked to thisValue, which you can then call later, as many times as you want.
Try It
- Create an object with a
nameproperty and a method that returns a greeting usingthis.name. Call the method normally, then pull the method out into its own variable and call it plainly. Explain in your own words why the second call fails. - Rewrite the
listRegularexample yourself: an object with an array property and a method that loops over it with a regularfunctioncallback, loggingthiseach time. Confirm you getundefined, then fix it by switching to an arrow function. - Write a plain function that uses
this.value, and an object{ value: 42 }. Call the function three different ways: with.call(), with.apply(), and by creating a bound version with.bind()and then calling that.
Recap
thisis decided by how a function is called, not where it’s written.- In a method call (
obj.method()),thisis the object before the dot. - In a plain function call,
thisisundefinedin strict mode. - Arrow functions don’t have their own
this, they use whateverthiswas already in scope around them. - A constructor call with
newsetsthisto the newly created object. .call()and.apply()run a function immediately with a chosenthis..bind()returns a new function withthislocked in for later.
Next lesson: objects, prototypes, and where classes fit in.