Vishwanath
@frozeninretroArrow functions are a syntactical sugar to regular functions. You can shorten your syntax and make things look pretty. But should you use them everywhere? It's definitely tempting to. However, arrow functions have certain rules:
-
Arrow functions don't have their own bindings to this, arguments, or super, and should not be used as methods.
-
Arrow functions cannot be used as constructors. Calling them with new throws a TypeError.
The first point is extremely important. Perhaps the most important rule of an arrow function is that it doesn't have its own this
binding, like a regular function. In this post, we'll only cover the first point.
Let's take an example of setTimeout
in an object method:
const obj = {
count: 10,
doSomethingLater() {
setTimeout(function () {
// here, this refers to the Window object
this.count++;
console.log(this.count);
}, 300);
},
};
obj.doSomethingLater();
// logs "NaN", because the property "count" is not in the window/global scope.
The setTimeout()
function creates a new execution context and the this
keyword inside the function refers to the global object instead of the object obj
. This is because in Javascript, the value of this
is determined by how a function is called, not where it is defined.
You might have seen this code - let self = this
OR let that = this
in older codebases. Wonder why it's used?
By assigning this
to the variable self/that
before the setTimeout call, we capture a reference to obj
, allowing us to access its count property later on.
const obj = {
count: 10,
doSomethingLater() {
// create a reference to "this",
// and store it in a variable named self
let self = this;
setTimeout(function () {
self.count++;
console.log(self.count);
}, 300);
},
};
obj.doSomethingLater(); // logs "11"
Or, we could use bind
to make sure the function executes in the proper scope.
const obj = {
count: 10,
doSomethingLater() {
setTimeout(
function () {
this.count++;
console.log(this.count);
}.bind(this),
300
);
},
};
obj.doSomethingLater(); // logs "11"
Why does the setTimeout function create a new execution context?
Confused about what execution context is? I suggest you read this article.
-
The
setTimeout()
function creates a new execution context because it uses a callback function that is executed after a specified delay. -
This execution context is different from the current execution context, which is the context in which
setTimeout()
was called.
In short;
Execution context of setTimeout is different from the execution context of callback func passed to setTimeout.
Instead, with arrow functions, we can simplify our code, and just modify our syntax to:
const obj = {
count: 10,
doSomethingLater() {
setTimeout(() => {
this.count++;
console.log(this.count);
}, 300);
},
};
obj.doSomethingLater(); // logs "11"
When an arrow function is used as the callback function passed to setTimeout()
, it inherits the this
value from the surrounding context lexically, which is the this
value of the doSomethingLater()
method of obj
.
And, we know, this
inside an object method refers to the object itself. So, arrow functions essentially "borrow" the this
value from the surrounding context.
Arrow functions are a great feature of ES6, but use them properly.
If you want a detailed explanation on arrow functions and all the rules, you can check the official MDN documentation.