The this keyword is one of the most important — and most misunderstood — concepts in JavaScript.
Many developers memorize rules like:
- “Arrow functions don’t have their own
this” - “Regular functions depend on how they are called”
- “Use arrow functions in callbacks”
But without understanding why JavaScript behaves this way internally, confusion quickly appears in real projects.
This article explains everything step by step:
- how
thisworks in regular functions, - how arrow functions “capture”
this, - what “lexical binding” actually means,
- how
call,apply, andbindaffectthis, - why classes behave differently,
- common mistakes,
- advantages and disadvantages of each approach.
After reading this guide, you should have a complete mental model of this in JavaScript.
1. What Is this?
In JavaScript, this is a special keyword that refers to an object associated with the current function execution.
The important thing is:
thisis determined differently depending on the type of function.
There are two completely different behaviors:
- Regular functions
-
thisdepends on how the function is called.
- Arrow functions
-
thisis taken from the surrounding scope when the function is created.
This difference is the key to understanding everything else.
2. this in Regular Functions
Basic Rule
For regular functions:
thisis determined at call time.
That means JavaScript looks at how the function was invoked.
3. Global Context
In Browsers
console.log(this);
In a browser outside strict mode:
window
Because the global object in browsers is window.
In Strict Mode
"use strict";
function test() {
console.log(this);
}
test();
Output:
undefined
In strict mode, regular functions called normally get undefined as this.
This behavior prevents many bugs.
4. Method Calls
When a function is called as a method of an object:
const user = {
name: "John",
sayHello: function () {
console.log(this.name);
}
};
user.sayHello();
Output:
John
Here:
this === user
because the function was called through user.
5. The Call-Site Rule
This is the most important rule for regular functions.
JavaScript determines this from the expression before the dot:
obj.method()
Inside method:
this === obj
6. Losing this
A very common mistake:
const user = {
name: "John",
sayHello() {
console.log(this.name);
}
};
const fn = user.sayHello;
fn();
Output in strict mode:
undefined
Why?
Because the function is no longer called as:
user.sayHello()
It is called as:
fn()
There is no owning object anymore.
7. call, apply, and bind
JavaScript provides methods for explicitly setting this.
call
function greet() {
console.log(this.name);
}
const user = { name: "Alice" };
greet.call(user);
Output:
Alice
call immediately invokes the function with a specific this.
apply
Works like call, but arguments are passed as an array.
fn.apply(obj, [arg1, arg2]);
bind
bind does not call the function immediately.
Instead, it creates a new function with permanently bound this.
function greet() {
console.log(this.name);
}
const user = { name: "Alice" };
const bound = greet.bind(user);
bound();
Output:
Alice
8. How JavaScript Internally Handles this
For regular functions, JavaScript creates this dynamically during execution.
Simplified idea:
function fn() {}
When called:
obj.fn()
JavaScript internally behaves roughly like:
fn.[[ThisValue]] = obj
The important part:
thisis NOT stored inside the function itself.
It is assigned every time the function runs.
That is why the same function can have different this values.
Example:
function show() {
console.log(this.name);
}
const user1 = { name: "John", show };
const user2 = { name: "Alice", show };
user1.show(); // John
user2.show(); // Alice
Same function.
Different this.
9. Arrow Functions: Completely Different Behavior
Arrow functions do NOT work like regular functions.
They do not create their own this.
Instead:
Arrow functions capture
thisfrom the surrounding lexical scope at creation time.
This is called lexical this.
10. What “Lexical” Actually Means
“Lexical” means:
determined by where code is written.
Not by how it is called.
This is the same principle used for variables in closures.
Example:
function outer() {
const x = 10;
return function inner() {
console.log(x);
};
}
inner remembers x from where it was created.
Arrow functions do the same with this.
11. How Arrow Functions Capture this
Example:
const user = {
name: "John",
regularFunction: function () {
console.log(this);
const arrow = () => {
console.log(this);
};
arrow();
}
};
user.regularFunction();
Output:
user
user
Why?
Because the arrow function does not create its own this.
When the arrow function is created, JavaScript looks outward:
- Is there a surrounding function with
this? - Yes →
regularFunction -
regularFunctionhasthis === user
The arrow function permanently stores that reference.
12. Internal Mental Model of Arrow Functions
A simplified mental model:
Regular function:
function () {}
creates:
its own this
Arrow function:
() => {}
behaves conceptually more like:
const capturedThis = this;
Then later:
use capturedThis forever
Important:
Arrow functions do not dynamically recalculate
this.
Ever.
13. Arrow Functions Ignore call, apply, and bind
This surprises many developers.
const user1 = { name: "John" };
const user2 = { name: "Alice" };
const arrow = () => {
console.log(this.name);
};
arrow.call(user1);
arrow.call(user2);
call does nothing.
Why?
Because arrow functions already captured this earlier.
They cannot be rebound.
14. Why Arrow Functions Were Added
Before arrow functions, developers constantly had problems with callbacks losing this.
Example:
const user = {
name: "John",
delayedHello: function () {
setTimeout(function () {
console.log(this.name);
}, 1000);
}
};
user.delayedHello();
Output:
undefined
because setTimeout calls the callback as a normal function.
Developers used ugly workarounds:
Old Solution #1: bind
setTimeout(function () {
console.log(this.name);
}.bind(this), 1000);
Old Solution #2: self = this
const self = this;
setTimeout(function () {
console.log(self.name);
}, 1000);
15. Arrow Functions Solve This Naturally
const user = {
name: "John",
delayedHello() {
setTimeout(() => {
console.log(this.name);
}, 1000);
}
};
user.delayedHello();
Output:
John
The arrow function captures this from delayedHello.
16. Arrow Functions and Closures
Arrow functions use the same mechanism as closures.
A closure remembers variables from outer scopes.
Arrow functions additionally remember:
thisargumentssuper
from the surrounding context.
17. Arrow Functions Do NOT Have Their Own:
thisargumentsprototypesupernew.target
This leads to important limitations.
18. Arrow Functions Cannot Be Constructors
This fails:
const Person = (name) => {
this.name = name;
};
new Person("John");
Error:
Person is not a constructor
Because arrow functions have no constructor behavior.
19. Arrow Functions as Object Methods: Dangerous
Bad example:
const user = {
name: "John",
sayHello: () => {
console.log(this.name);
}
};
user.sayHello();
Output:
undefined
Why?
Because the arrow function does NOT get:
this === user
Instead, it captures this from the outer scope.
Usually global scope.
This is one of the biggest beginner mistakes.
20. When Arrow Functions SHOULD Be Used
Arrow functions are excellent for:
- callbacks,
- asynchronous code,
- array methods,
- preserving surrounding
this.
Example:
items.map(item => item.id);
or:
button.addEventListener("click", () => {
this.handleClick();
});
21. When Regular Functions SHOULD Be Used
Regular functions are better when:
- you need dynamic
this, - you need object methods,
- you use constructors,
- you need
arguments, - you want reusable method behavior.
Example:
const user = {
name: "John",
sayHello() {
console.log(this.name);
}
};
22. Classes and this
In classes, methods are regular functions.
class User {
constructor(name) {
this.name = name;
}
sayHello() {
console.log(this.name);
}
}
But class methods often lose this in callbacks.
23. Common React Example
class App {
constructor() {
this.count = 0;
}
increment() {
this.count++;
}
}
Problem:
button.addEventListener("click", app.increment);
this is lost.
Solution:
this.increment = this.increment.bind(this);
OR:
increment = () => {
this.count++;
}
The arrow function captures the instance this.
24. Performance Considerations
Regular Methods
class User {
sayHello() {}
}
Method stored once on prototype.
Efficient memory usage.
Arrow Methods in Classes
class User {
sayHello = () => {}
}
New function created per instance.
More memory usage.
But easier this handling.
Trade-off:
| Feature | Regular Method | Arrow Method |
|---|---|---|
Dynamic this
|
Yes | No |
| Prototype sharing | Yes | No |
| Easier callbacks | No | Yes |
| Memory efficient | Yes | Less |
Can use new
|
Yes | No |
25. The Ultimate Mental Model
Regular Functions
Think:
“
thisdepends on HOW the function is called.”
Arrow Functions
Think:
“
thisis permanently copied from the surrounding scope.”
Not dynamically determined.
Not changeable later.
26. The Most Important Difference
Regular function:
function () {}
asks:
“Who called me?”
Arrow function:
() => {}
asks:
“Where was I created?”
That single difference explains almost all behavior.
27. Best Practices
Use Arrow Functions For
- callbacks,
- promises,
- timers,
- event handlers needing outer
this, - functional programming,
- short utility functions.
Use Regular Functions For
- object methods,
- prototypes,
- constructors,
- dynamic context,
- reusable APIs.
28. Common Interview Question
What is the difference between:
function () {}
and
() => {}
Correct deep answer:
Regular functions get
thisdynamically at call time.Arrow functions lexically capture
thisfrom the surrounding scope when created.
Everything else is a consequence of that rule.
29. Final Summary
Regular Functions
- Have their own
this -
thisdetermined by call-site - Can be rebound
- Can be constructors
- Better for methods
Arrow Functions
- No own
this - Capture outer
this - Cannot be rebound
- Cannot be constructors
- Better for callbacks
30. Final Thought
Most confusion about this disappears once you stop thinking of arrow functions as “shorter functions”.
They are not just syntax sugar.
They are a fundamentally different function model.
Regular functions are dynamic.
Arrow functions are lexical.
Understanding this distinction is the key to mastering JavaScript.
Top comments (0)