Arrow functions were first introduced in ES6 (ES2015) in 2015.
While looking cleaner there are some differences when compared to regular functions which may trip you up if you do not understand them.
Here is a guide on how to use arrow functions.
First of all lets compare an arrow function with a regular function:
// ES6 Arrow function
const hello1 = () => {console.log('Hello world')}// ES5 Function
const hello2 = function() {
console.log('Hello world')
}hello1(); // Hello world
hello2(); // Hello world
The first obvious difference is that we don’t have to write the word function
and the second is that we use =>
to move into the function body. It looks better, I think most will agree.
No more return
or parentheses
There are also cases where we no longer need the return
keyword or parentheses. Consider the following example. We have an array of years and we want to find out how long ago an event happened.
const years = [1970, 1984, 1945, 1998, 2010];const yearsAgoES5 = years.map(function(year) {
return 2020 - year
});const yearsAgoES6 = years.map(year => 2020 - year);console.log(yearsAgoES5); // [ 50, 36, 75, 22, 10 ]
console.log(yearsAgoES6); // [ 50, 36, 75, 22, 10 ]
Same result, less code.
We no longer need use the return
keyword to return the number of years or use parentheses around year
. You would however require parentheses if we had more than one argument such as the index
of which map also gives us access to.
We could also use curly braces if wanted to, and if we did we would then require the return
keyword again:
const yearsAgoES6 = years.map((year, index) => {
return `${index}: ${2020 - year}`
});
Lexical scope (this)
One of the main differences between ES5 functions and ES6 arrow functions is that an arrow function shares the this
keyword with its surroundings, that is their lexical scope.
ES5 functions however use values based on their context, that is the object that calls them.
Lets consider and an ES5 example where we have an object with a function property that wants to get access to other properties of the object.
const objES5 = {
name: 'Sam Orgill',
job: 'Web developer',
website: 'samorgill.com',
print: function() {
console.log(this.name); // Sam Orgill
setTimeout(function() {
console.log(this.name); // undefined
}, 1000);
}
}objES5.print();
We can see when the print function
is called it correctly logs out Sam Orgill
. That is because when the print()
function is called it shares the context of the object.
When we into the setTimeout()
function however it no longer shares the context of the objES5
object, it instead shares the context of the global window
object in your browser.
It is almost always the case however that when you wrote code like this that you meant to refer to objES5.name
and so I actually consider this a bug in ES5 though others would disagree.
One common hack to get around this was to store this
in a new variable called self
within the print
function and then use it within the setTimeout
so that it could get access to the objects context.
const objES5 = {
name: 'Sam Orgill',
job: 'Web developer',
website: 'samorgill.com',
print: function() {
var self = this;
setTimeout(function() {
console.log(self.name); // Sam Orgill
}, 1000);
}
}objES5.print();
Arrow functions and this
Arrow functions in ES6 share the lexical this
value of its surroundings (objES6
). What this means is that everything within objES6
is within it’s lexical scope. So if we convert the setTimeout()
function to an arrow function we can freely reference objES6.name
.
const objES6 = {
name: 'Sam Orgill',
job: 'Web developer',
website: 'samorgill.com',
print: function() {
console.log(this.name); // Sam Orgill
setTimeout(() => {
console.log(this.name); // Sam Orgill
}, 1000);
}
}objES6.print();
When not to use an arrow function
But what about our print
function? Can we also convert this to an arrow function? Of course we can. But it would break.
const objES6 = {
name: 'Sam Orgill',
job: 'Web developer',
website: 'samorgill.com',
print: () => {
console.log(this.name); // undefined
setTimeout(() => {
console.log(this.name); // undefined
}, 1000);
}
}objES6.print();
The reason this example logs out undefined
is because an arrow function takes on the this
value of its surroundings, which in the case of the first console.log
would be the window
global object and not objES6
, and so the second console.log
also shares the global window
object which of course doesn't contain our name
.
Want to take your web development skills to the next level?
I created Angular Essentials, a course designed for people with NO Angular experience. Get 90% off now 🚀