Posts
JavaScript this and Arrow Functions Explained
JavaScript functions do not all decide this the same way. Regular functions usually get this from how they are called. Arrow functions do not create their own this at all. This post is about two questions: Where does a …
In this article
JavaScript functions do not all decide this the same way.
Regular functions usually get this from how they are called. Arrow functions do not create their own this at all.
This post is about two questions:
- Where does a regular function get
thisfrom? - Why does an arrow function behave differently?
this is one of the easiest JavaScript concepts to misunderstand because it looks like scope.
It is not scope.
Scope answers:
Where can this name be found?
this answers:
What object is this function running with?
Those questions are related, but they are not the same question.
Scope Is Not this
Scope is about where JavaScript looks for a name.
outer scope
+-- function scope
+-- block scope
If code cannot find a variable in the current scope, JavaScript can look outward through the surrounding scopes.
That explains variables like name, count, or message.
this plays by a different rule.
For regular functions, this usually depends on how the function is called.
Regular Functions Get this From the Call
const user = {
name: "Ada",
sayName: function() {
console.log(this.name);
},
};
user.sayName();
// Ada
Because the function is called as user.sayName(), this points to user.
The object on the left side of the call matters.
user.sayName()
^^^^
this points here
Now remove that call-site.
const user = {
name: "Ada",
sayName: function() {
console.log(this.name);
},
};
const sayName = user.sayName;
sayName();
// undefined in sloppy browser scripts
// often TypeError in strict mode or modules
Same function.
Different call.
Different this.
The function did not forget about user. It was simply no longer called through user.
Arrow Functions Do Not Have Their Own this
Arrow functions are useful, but they are not regular functions with shorter syntax.
An arrow function does not create its own this.
It captures this from the surrounding lexical scope where it was created.
An arrow function does not go looking for a new this later.
It keeps the this that was already available where the arrow function was created.
That is the important correction: an arrow function is not asking the call-site for a new this.
It already has its this from where it was created.
regular function:
this = decided when called
arrow function:
this = borrowed from where it was created
That is the difference to keep in your head.
Arrow Functions Inside Methods
Arrow functions are often useful inside methods because they keep the method’s this.
const counter = {
count: 0,
start: function() {
setTimeout(() => {
this.count += 1;
console.log(this.count);
}, 1000);
},
};
counter.start();
// 1
Here is what happens:
counter.start()is called.- Inside
start(),thisiscounter. - The arrow function is created inside
start(). - The arrow function captures that
this.
So when the timer runs later, the arrow function still uses counter.
counter.start()
+-- this = counter
+-- arrow callback
+-- captures this from start()
That is the part that makes arrows helpful in callbacks.
Regular Functions Inside Methods
Compare that with a regular function.
const counter = {
count: 0,
start: function() {
setTimeout(function() {
this.count += 1;
console.log(this.count);
}, 1000);
},
};
counter.start();
// not 1
The regular function passed to setTimeout gets its own this, based on how setTimeout calls it.
It does not automatically inherit this from start().
In a browser that may point at window. In Node it may be a timer object. In strict mode or module-shaped code, you may run into undefined instead.
The exact runtime detail can vary, but the core lesson does not:
A regular callback does not automatically keep the outer method’s
this.
Do Not Use Arrow Functions For Object Methods
This is the other side of the rule.
const user = {
name: "Ada",
sayName: () => {
console.log(this.name);
},
};
user.sayName();
// not Ada
An arrow function does not get this from user just because it is stored as a property on user.
It captured this from the surrounding scope where the object was created.
If you want this to mean the object, use a regular method.
const user = {
name: "Ada",
sayName() {
console.log(this.name);
},
};
user.sayName();
// Ada
The method syntax above creates a regular method, so this comes from the call-site.
The Practical Rule
Keep these separate:
- Scope answers: “Where can this name be found?”
- Regular function
thisanswers: “How was this function called?” - Arrow function
thisanswers: “What wasthiswhere this arrow was created?”
So when you are debugging an arrow function, do not start by asking, “Who called this?”
Start by asking, “Where was this arrow function created?”
That is the question that usually gets you unstuck.
Related JavaScript Posts
These topics build on each other:
- JavaScript var, let, and const Explained covers the declaration boundaries that shape scope.
- JavaScript Hoisting Explained explains what declarations are available before execution reaches them.
- JavaScript Closures and Modified Closures Explained covers the lexical scope behavior that makes arrow function
thiseasier to reason about.
-Rob