Understanding the this Keyword in JavaScript
JavaScript has a special keyword called this.
At first, this can feel confusing because its value is not fixed. It can change depending on where the code is running and, most importantly, how a function is called.
A simple way to think about this is:
thisis like a pronoun. Its meaning depends on the context in which it is used.
For functions, the calling context is especially important.
Let's understand it step by step.
What Does this Represent?
this is a special keyword provided by JavaScript.
It refers to a value determined by the execution context in which the code is running.
You can think of it as a hidden reference available while JavaScript executes code.
For example:
console.log(this);
The result depends on where this code is running.
This is why we should not memorize:
"
thisalways means the current object."
That is not true.
A better rule is:
For regular functions,
thisis mainly determined by how the function is called.
this in the Global Context
Before understanding objects and functions, we need to understand what happens at the top level of a program.
Different JavaScript environments have different global objects.
For example:
| Environment | Global Object |
|---|---|
| Browser | window |
| Node.js | global |
| Web Worker | self |
JavaScript provides a standard way to access the global object regardless of the environment:
globalThis
So we can think of globalThis as:
"Give me the global object of the JavaScript environment I'm currently running in."
What Is globalThis?
globalThis is a standard JavaScript property that refers to the global object.
Instead of remembering different names such as window, global, or self, we can use:
console.log(globalThis);
For example, in a browser:
console.log(globalThis === window);
Output:
true
In Node.js:
console.log(globalThis === global);
Output:
true
So:
Browser
│
├── window
└── globalThis ──► same global object
Node.js
│
├── global
└── globalThis ──► same global object
This is an important distinction:
globalThisalways refers to the global object, butthisdoes not always refer to the global object.
Global this in the Browser
In a traditional browser script, if we write:
console.log(this);
console.log(globalThis);
we get the browser's global object:
Window { ... }
Window { ... }
And:
console.log(this === globalThis);
gives:
true
So in a normal browser script:
this
│
▼
window
│
▲
│
globalThis
Here:
this === window
and:
globalThis === window
are both true.
Global this in Node.js
Node.js has an important difference.
In a CommonJS module, if we write:
console.log(this);
console.log(globalThis);
the output is conceptually:
{}
global { ... }
Why are they different?
Because a CommonJS file is wrapped by Node.js in a function.
Conceptually, Node.js does something similar to:
(function (exports, require, module, __filename, __dirname) {
// Your code
});
The exact internal implementation is more complicated, but this simplified model helps us understand the behavior.
The wrapper is effectively called with module.exports as its this value.
Conceptually:
wrapper.call(module.exports);
Therefore, at the top level of a CommonJS module:
this === module.exports
Since module.exports starts as an empty object, we commonly see:
{}
while:
globalThis
still refers to Node's global object.
So:
Node.js CommonJS
this
│
▼
module.exports
│
▼
{}
globalThis
│
▼
global
Therefore, in CommonJS:
console.log(this === globalThis);
is:
false
This is one of the most important differences between browser scripts and Node.js CommonJS modules.
What About Strict Mode?
Strict mode changes how this behaves inside regular functions.
Consider:
function ranveerWithNoScript() {
return this;
}
console.log(ranveerWithNoScript());
If this regular function is called directly:
ranveerWithNoScript();
then:
Non-strict mode
In a browser, this becomes the global object:
window
In a Node.js CommonJS module, a regular function called without an explicit receiver can have this as the global object:
global
Strict mode
"use strict";
function ranveerWithNoScript() {
return this;
}
console.log(ranveerWithNoScript());
Now:
undefined
So a useful rule is:
Regular function called directly
Non-strict mode
│
├── Browser ──► window
└── Node.js ──► global
Strict mode
│
└──► undefined
A Note About Top-Level this
We should be careful not to mix up top-level this with this inside a function.
For example:
console.log(this);
is top-level this.
But:
function test() {
console.log(this);
}
test();
is this inside a function.
These can behave differently.
Also, JavaScript modules (ES modules) have different top-level this behavior from traditional scripts/CommonJS modules.
For example, in an ES module:
console.log(this);
results in:
undefined
So the environment and module system matter.
For beginners, the important lesson is:
Always consider where the code is running and whether it is a regular script, CommonJS module, or ES module.
this Inside Objects
Now let's move to the most common use of this.
Consider this object:
const actor = {
name: "Ranveer",
bow() {
return `${this.name} takes a bow`;
}
};
console.log(actor.bow());
Output:
Ranveer takes a bow
Why?
Because we called:
actor.bow();
The object on the left side of the dot is the calling object.
actor.bow()
│
│
└── left of the dot
│
▼
this
So inside bow():
this === actor
Therefore:
this.name
means:
actor.name
which is:
Ranveer
The "Left of the Dot" Rule
For a beginner, this is a very useful mental model:
actor.speak();
The object before the dot becomes this for that regular method call.
actor.speak()
│
▼
this = actor
Another example:
const person = {
name: "Om",
greet() {
console.log(this.name);
}
};
person.greet();
Here:
person.greet()
│
▼
this = person
So:
this.name
becomes:
person.name
and prints:
Om
Calling Context Can Change this
Now comes the most important part.
The function itself does not permanently belong to the object.
Consider:
const actor = {
name: "Ranveer",
bow() {
return `${this.name} takes a bow`;
}
};
console.log(actor.bow());
Here:
actor.bow()
means:
this = actor
But now look at this:
const detachedBow = actor.bow;
console.log(detachedBow());
We copied the function into another variable and called it directly.
Now there is no:
actor.bow()
Instead, there is:
detachedBow()
So the original actor calling context is gone.
Before
actor.bow()
│
▼
this = actor
After detaching
detachedBow()
│
▼
no actor receiver
│
▼
this depends on
strict/sloppy mode
The function body remains the same, but its calling context has changed.
This is why:
thisbelongs to the way a function is called, not permanently to where the function was originally written.
Regular Functions Have Their Own this
Regular functions can receive their own this binding depending on how they are called.
For example:
const myFunctionOne = function () {
console.log(this);
};
myFunctionOne();
If called directly in non-strict mode, this can refer to the global object.
But in strict mode:
"use strict";
const myFunctionOne = function () {
console.log(this);
};
myFunctionOne();
this will be:
undefined
So regular functions are dynamic with respect to this.
Arrow Functions Are Different
Arrow functions behave differently.
An arrow function does not create its own this binding.
Instead, it gets this from its surrounding lexical scope.
Consider:
const filmSet = {
crew: "Spot boys",
prepareProps() {
console.log(`Outer this.crew: ${this.crew}`);
function arrangeChairs() {
console.log(`Inner this.crew: ${this.crew}`);
}
arrangeChairs();
const arrangeLights = () => {
console.log(`Arrow this.crew: ${this.crew}`);
};
arrangeLights();
}
};
filmSet.prepareProps();
The output in a strict context is conceptually:
Outer this.crew: Spot boys
Inner this.crew: undefined
Arrow this.crew: Spot boys
Let's understand why.
What Happens to this in prepareProps()?
We call:
filmSet.prepareProps();
The object on the left side of the dot is:
filmSet
Therefore:
this === filmSet
inside the regular method prepareProps().
So:
this.crew
is:
filmSet.crew
which gives:
Spot boys
What Happens Inside arrangeChairs()?
Inside prepareProps() we have:
function arrangeChairs() {
console.log(`Inner this.crew: ${this.crew}`);
}
arrangeChairs();
This is a regular function.
And it is called directly:
arrangeChairs();
There is no:
filmSet.arrangeChairs();
Therefore, it does not automatically inherit the this from prepareProps().
In strict mode:
arrangeChairs()
│
▼
this = undefined
Therefore:
this.crew
does not give us "Spot boys".
What Happens Inside the Arrow Function?
Now consider:
const arrangeLights = () => {
console.log(`Arrow this.crew: ${this.crew}`);
};
arrangeLights();
Arrow functions do not create their own this.
Instead, they lexically inherit this from their surrounding scope.
The surrounding method is:
prepareProps()
and inside that method:
this === filmSet
Therefore the arrow function also uses that this.
filmSet.prepareProps()
│
▼
this = filmSet
│
│ lexical inheritance
▼
arrangeLights()
│
▼
this = filmSet
So:
this.crew
prints:
Spot boys
Regular Function vs Arrow Function
This gives us a very useful comparison:
| Regular Function | Arrow Function |
|---|---|
Has its own this behavior |
Does not create its own this |
this depends on how it is called |
this comes from surrounding scope |
Can have different this values for different calls |
Lexically inherits this |
| Useful for object methods | Often useful for callbacks |
So:
function normalFunction() {
console.log(this);
}
has its own this behavior.
But:
const arrowFunction = () => {
console.log(this);
};
does not create a new this.
Another Example: Same Function, Different this
Let's make this even clearer.
const person1 = {
name: "Om",
greet: function () {
console.log(this.name);
}
};
const person2 = {
name: "Rahul"
};
person2.greet = person1.greet;
person1.greet();
person2.greet();
Output:
Om
Rahul
Why?
The function is essentially the same:
function () {
console.log(this.name);
}
But the calling context changes.
First:
person1.greet();
So:
this = person1
Second:
person2.greet();
So:
this = person2
The same function body can therefore work with different this values.
Same Function
│
├── person1.greet()
│ │
│ └── this = person1
│
└── person2.greet()
│
└── this = person2
This is why thinking about the caller/calling context is so useful.
A Practical Example of Losing this
Consider:
global.name = "Gajanan Mundkar";
const obj = {
name: "ommie",
demo() {
return this.name;
}
};
const demo2 = obj.demo;
console.log(demo2());
When we call:
obj.demo();
we have:
this = obj
so:
this.name
is:
ommie
But after:
const demo2 = obj.demo;
we call:
demo2();
Now the function is detached from the object.
In non-strict mode, a direct call can make this refer to the global object.
Therefore, depending on the environment and mode, this.name can refer to the global name.
This demonstrates an important point:
Assigning an object method to another variable does not preserve the original object as
this.
The Big Picture
We can summarize the major cases like this:
JavaScript
│
▼
this
│
┌────────────────┼────────────────┐
│ │ │
▼ ▼ ▼
Global Context Regular Function Arrow Function
│ │ │
│ │ └── lexical this
│ │
│ └── depends on call
│
└── depends on environment/module type
For regular functions:
How was the function called?
│
▼
Determine `this`
For arrow functions:
Where was the arrow function created?
│
▼
Take `this` from surrounding scope
Quick Rules to Remember
Instead of memorizing hundreds of rules, these are the important beginner-level rules I use to reason about this.
1. this depends on context
console.log(this);
can behave differently depending on where the code runs.
2. globalThis means the global object
console.log(globalThis);
It gives you the global object of the current JavaScript environment.
Browser → globalThis === window
Node.js → globalThis === global
3. In a regular object method call
actor.speak();
the object before the dot is the useful mental model for this:
actor.speak()
│
▼
this
4. Detaching a method can change this
const speak = actor.speak;
speak();
The original actor is no longer the receiver of the call.
5. Regular functions have their own this behavior
function test() {
console.log(this);
}
Its this depends on how test() is called.
6. Arrow functions don't create their own this
const test = () => {
console.log(this);
};
They inherit this lexically from their surrounding scope.
Final Takeaway
The biggest mistake beginners make with this is thinking:
"
thismeans the object where the function was created."
Instead, for regular functions, a better mental model is:
Look at how the function is being called.
For example:
actor.speak();
actor
│
▼
this
But:
const speak = actor.speak;
speak();
is a different call with a different calling context.
And for arrow functions:
Arrow functions don't create their own
this; they inherit it from their surrounding lexical scope.
Finally, don't confuse this with globalThis.
globalThis
│
▼
Global object of the environment
this
│
▼
Value determined by the current context
So the easiest way to remember the whole concept is:
`this`
│
┌────────────┴────────────┐
│ │
Regular Function Arrow Function
│ │
▼ ▼
How is it called? Where was it created?
│ │
▼ ▼
Calling context Lexical surrounding scope
Once you start looking at who is calling the function and how it is being called, this becomes much less mysterious.
