TL;DR:
In JavaScript, the value ofthis
depends entirely on how a function is called, not where it was defined.
It can reference the global object (window
/global
), the calling object,undefined
(in strict mode), or be explicitly bound using.bind()
,.call()
, or arrow functions.
In this post, we cover:
- What
this
points to in different contexts: object methods, global scope, functions, classes, and callbacks- How
this
behaves differently in strict mode- The use of
.bind()
and.call()
to controlthis
explicitly- Why arrow functions can be lifesavers in callback scenarios
- How
this
works with constructor functions and classes By the end, you’ll understand the most misunderstood keyword in JavaScript with clarity.
Introduction
The question that could bring world peace: "What is the value of this
?"
If you’ve worked with JavaScript for a while, you’ve probably hit this dreaded error:
Cannot read property 'value' of undefined
If you haven’t seen it yet — don’t worry young padawan, your time will come. 😅
In this post, I’ll break down what this
really is in JavaScript. Spoiler: it’s all about who’s calling the function — not where or how it’s written. We’ll explore this
in object methods, top-level functions, strict mode, constructors, and even callback hell. Strap in — it’s going to clear up a lot.
In short, in JS, this
points to the object that is executing the current function — in other words, the object that defines the execution context of that function.
Said like this, it may sound tricky to grasp, but let’s go through some practical examples.
this
in the context of methods
We already know that when a function is part of an object, it’s called a method. In this scenario, it’s easy to deduce what object will be referenced by this
, since the function is part of the object itself.
const car = {
model: "MCL39",
team: "McLaren",
showThis() {
console.log(this)
}
}
car.showThis();
// Object { model: "MCL39", team: "McLaren", showThis: showThis() }
No big mystery here — this
points to the object that called the function, which in this case is const car
.
In most cases, you can figure this out by using the left-hand rule: the object to the left of the function invocation is usually the context.
this
in the context of functions
Okay, but what if my function isn’t being called by an object? What if I’m not even inside a function? What is this
then? Well, everything in JS is an object — everything.
So, if in your code, script, or app you’re using a function that isn’t part of an object you created, then it’s part of JS’s global object. This global object can be window
in the browser or global
in NodeJS.
Here’s an example:
console.log(this); // Window {...}
if (true) {
console.log(this); // Window {...}
}
function aFunction() {
console.log(this);
}
aFunction(); // Window {...}
In this case, we see three examples where the context is the same.
The first
console.log(this)
runs directly in the browser’s console — its execution context is the globalwindow
object.The second one is inside an
if()
block — again,this
points towindow
. This happens because blocks don’t define context. They define variable scope, but not object context. In the end, it’s just another block belonging to the global object.The third one is inside the
aFunction()
function, but it’s being invoked directly in the console, by the browser’swindow
. So, its context is alsowindow
.
To reinforce what we said earlier: as a general rule — with a few exceptions we’ll cover later — the context of this
belongs to whoever is calling the function.
We can prove this by checking the window
object:
console.log(this);
/*
Window {
aFunction(): function aFunction()
}
*/
window.aFunction(); // Window {...}
Notice that the window
object contains the aFunction()
we defined. That’s because we created the function directly in the browser console, which defaults to the window
context.
this
and strict mode
If you’re using “strict mode”, the return will be undefined
. Strict mode is a safeguard that prevents direct access to the global object through this
. It’s used by default in React and Node environments, for example.
function aFunction() {
'use strict';
console.log(this);
}
const obj = {
aFunction
}
obj.aFunction(); // Object {...}
aFunction(); // undefined
Here, we explicitly use strict mode inside the function. That way, the global object can’t be referenced through this
.
But when we place the same function inside an object and call it through that object, this
correctly references the object.
When we invoke the function directly in the console, we get undefined
— because strict mode forbids access to the global object (window
in this case).
But this can cause problems. What if we need to execute a function using an object we can’t modify?
Binding and this
Imagine I have my accelerate()
function and I want it to be executed using my car
object — but I don’t have permission to add the function directly to the object.
To solve this, there are some Function API methods that let us manually bind the object to the function’s this
.
Let’s start with Function.bind()
:
const car = {
speed: "250km/h"
}
function accelerate() {
return `Accelerating at ${this.speed}`;
}
const boundFunction = accelerate.bind(car);
boundFunction(); // Accelerating at 250km/h
Function.bind()
creates a new function whose this
context is explicitly set to the object passed as parameter. This new function can then be stored and reused.
Now, what if the function receives arguments? There are two ways: you can pass the argument in the bind()
call or call the bound function with arguments later.
const car = {
speedKm: "250km/h",
speedMi: "155mph"
}
function accelerate(isMPH) {
return isMPH
? `Accelerating at ${this.speedMi}`
: `Accelerating at ${this.speedKm}`;
}
const boundWithArg = accelerate.bind(car, false);
const boundWithoutArg = accelerate.bind(car);
boundWithArg(); // Accelerating at 250km/h
boundWithoutArg(true); // Accelerating at 155mph
boundWithoutArg(false); // Accelerating at 250km/h
// If bind was used with a fixed argument
boundWithArg(true); // Accelerating at 250km/h
boundWithArg(false); // Accelerating at 250km/h
When you bind with an argument, that argument is fixed and can’t be changed later — so choose the method that fits your case best.
If you don’t need to reuse the function and just want to run it once, use Function.call()
:
const car = {
speedKm: "250km/h",
speedMi: "155mph"
}
function accelerate(isMPH) {
return isMPH
? `Accelerating at ${this.speedMi}`
: `Accelerating at ${this.speedKm}`;
}
accelerate.call(car, true); // Accelerating at 155mph
There’s not much difference between bind
and call
in this simple case — but these methods are much more powerful than shown here. This was just to explain the binding concept so we can better understand this
.
this
and constructors/classes
We already explored constructors and classes in a previous post — and you’ve probably noticed how frequently we use this
inside them.
class Car {
constructor(model, team) {
this.model = model;
this.team = team;
}
}
Here’s a good opportunity to reinforce why this
is so important. A class is instantiated with the new
keyword. In that earlier post, I explained that the reason this
exists inside a constructor is because the constructor “creates” a this
context for the object. The example I used was:
function Pet(name, ownerName) {
// const this = {};
this.name = name;
this.ownerName = ownerName;
this.identify = () => {
return `${this.name} belongs to ${this.ownerName}`;
}
// return this;
}
But now we can go further: when we use the new
keyword, JavaScript automatically binds the this
inside the constructor/class to the new object being created.
Back to our code:
class Car {
constructor(model, team) {
this.model = model;
this.team = team;
}
}
In this code, notice how the argument name and the class property name are the same. The this.model
on the left refers to the instance’s property, while the model
on the right is the constructor argument.
Also, when we instantiate an object from this class, this
will refer to the new object:
class Car {
constructor(model, team) {
this.model = model;
this.team = team;
}
showThis() {
console.log(this);
}
}
const car = new Car("MCL39", "McLaren");
car.showThis(); // Object { model: "MCL39", team: "McLaren" }
this
and Callbacks
Let’s simulate the 200ms reaction time a racing driver has before pressing the throttle. We’ll use setTimeout()
— which takes two parameters: a delay time and a function to run once that time has passed.
class Car {
constructor(model, team) {
this.model = model;
this.team = team;
}
accelerate() {
setTimeout(function() {
console.log(`Accelerating at ${this.model}`);
}, 200);
}
}
const car = new Car("MCL39", "McLaren");
car.accelerate(); // Accelerating at undefined
Oops… this.model
is undefined? Aren’t we inside the car
object?
That’s the catch: we’re inside a callback function, which is just a regular function — and as explained earlier, in normal functions, this
refers to the global object (or undefined
in strict mode).
Let’s verify:
class Car {
constructor(model, team) {
this.model = model;
this.team = team;
}
showThis() {
setTimeout(function() {
console.log(this);
}, 200);
}
}
const car = new Car("MCL39", "McLaren");
car.showThis(); // Window {...} or undefined
There are two ways to fix this:
Option 1: Explicit binding with bind()
class Car {
constructor(model, team) {
this.model = model;
this.team = team;
}
accelerate() {
setTimeout(function() {
console.log(`Accelerating at ${this.model}`);
}.bind(this), 200);
}
}
const car = new Car("MCL39", "McLaren");
car.accelerate(); // Accelerating at MCL39
We’re explicitly binding the this
context to the object when passing the function into setTimeout()
.
Option 2: Using an arrow function
class Car {
constructor(model, team) {
this.model = model;
this.team = team;
}
accelerate() {
setTimeout(() => {
console.log(`Accelerating at ${this.model}`);
}, 200);
}
}
const car = new Car("MCL39", "McLaren");
car.accelerate(); // Accelerating at MCL39
Arrow functions don’t create their own this
— they inherit it from the outer context, which in this case is the accelerate()
method. That’s why this solution works so well.
For a deeper explanation of why this works and why I recommend arrow functions in this context, check the arrow functions post — it covers the nuance in detail.
Wrapping up
JavaScript’s this
can be confusing, but with some practical rules and examples, you can master it:
-
this
refers to the calling object in most cases - In strict mode,
this
isundefined
unless explicitly bound - In global functions, it defaults to the global object (window/global)
- Arrow functions don’t have their own
this
, they inherit it from the parent scope -
.bind()
,.call()
, and.apply()
give you explicit control overthis
Knowing how to use this
effectively will help you write more predictable, safer code — especially in modern frameworks like React or when designing your own reusable JavaScript modules.
💬 Have you ever been bitten by a confusing this
bug?
Let me know in the comments — and let’s help each other write better JavaScript.
📬 And don’t forget: follow me @matheusjulidori for more hands-on JavaScript tips, patterns, and dev-friendly content, every week! Share this content with your peers if you enjoyed it!