Arrow Functions
In this lesson, you will learn the new concise syntax for creating functions, which helps us with scoping.
We'll cover the following
One of the most popular new features in ECMAScript 2015 is arrow functions. Is it so popular because of the new cleaner syntax or for sharing this with the parent scope? Maybe both. Let’s look into them in more detail.
Syntax
First arrow functions were introduced to provide a shorter function syntax. The syntax can look a bit different depending on the function. Let’s see some options!
Here is a function written in ES5 syntax:
var sum = function(a, b) {
return a + b;
}
sum(1, 2) // -> 3
And here is the same function as an arrow function:
const sum = (a, b) => a + b;
sum(1, 2) // -> 3
As you can see, the arrow function makes the code much more concise when we have one-line expressions. Due to implicit return, we can omit the curly braces and return
the keyword.
If we have multiple lines in the function, we always have to use the curly braces and return
:
const printSum = (a, b) => {
const sum = a + b;
console.log('Answer: ' + sum);
return sum;
}
printSum(1, 2); // -> Answer: 3
Parentheses are optional when only one parameter is present:
const square = n => n * n;
square(3); // -> 9
Arrow functions are sometimes called fat arrow functions because of the token
=>
that resembles a fat arrow.
We commonly use arrow functions when using array methods. Here, we create a new array with the ages of the players by mapping through the player array:
const players = [
{ name:'Michael', age:44},
{ name:'Karl', age:33},
{ name:'Lisa', age:37}
];
const ages = players.map(player => player.age); // [44, 33, 37]
Array functions really shine here!
This
You have seen how nice the new syntax can be. However, there is another better reason to use arrow functions, and that is the sometimes-confusing behavior of the this
keyword in JavaScript. Arrow function doesn’t depend on where it’s declared but on how and where the function is called, its execution context.
In the global scope, the value of
this
is always thewindow
object.
Regular functions
Functions have their own execution context and with it comes their own this
. Understanding these concepts is not easy, but it is important.
Hopefully, some example code can make it clearer. To show the difference in execution context, we use setTimeout
, a native JavaScript function, which calls a function after a set delay in milliseconds.
// ES5
function Age() {
this.age = 42;
setTimeout(function() {
console.log(this.age); // -> undefined
}, 1000);
}
var a = new Age();
If we run this code, we get undefined
logged to the console because the function inside setTimeout
has its own execution context. So, we are not accessing this.age
of the Age
function but of the one inside the function where we make the this.age
call, which in this case is setTimeout
. And setTimeout
hasn’t assigned a value to age
.
To access the context of the parent function, developers often reassign the this
in the outer function to a variable commonly named that
or self
.
// ES5
function Age() {
var that = this;
that.age = 42;
setTimeout(function() {
console.log(that.age); // -> 42
}, 1000);
}
var a = new Age();
This way, we can access the this
of the parent function. However, you can see how it can be confusing.
Another option is to use
function {}.bind(this)
.
Arrow functions
Arrow functions, however, share the lexical scope with their parent. This means that they use this
from the code that contains the arrow function.
// ES6
function Age() {
this.age = 42;
setTimeout(() => {
console.log(this.age); // -> 42
}, 1000);
}
const a = new Age();
So, you can see how arrow functions help to make the code cleaner and easier to read.
Should we always use arrow functions?
Arrow functions are great, but regular functions are still useful. Let’s look at some examples of when we should not use arrow functions.
Methods
Arrow functions are best suited for non-method functions. Let’s see what happens when we try to use them as methods. In this example, we create an object called blog
with a method like
.
const blog = {
likes: 0,
like: () => {
this.likes++;
}
}
One would think that every time we call blog.like()
the attribute blog.likes
increases by one. However, the value of likes will sadly remain at zero.
This time, the parent scope is the window object and not the blog object. Therefore, invoking the like()
method would attempt to increment the property likes
on the
window object.
If we use the traditional syntax instead, it will work as expected:
const blog = {
likes: 0,
like: function() {
this.likes++;
}
}
Even better, another new feature in ES6 is shorthand method syntax:
const blog = {
likes: 0,
like() {
this.likes++;
}
}
Constructor
An arrow function cannot be used as a constructor. It will throw an error when used with new
:
const PrintDouble = (x) => console.log(2 * x);
new PrintDouble(2);
// -> TypeError: PrintDouble is not a constructor
We can use a function expression instead:
const PrintDouble = function(x) {
console.log(2 * x);
};
new PrintDouble (2); // -> 4
Conclusion
Arrow functions are a great new addition in ES6 that brings us a syntactically compact alternative to a regular function. An arrow function does not have its own this
. The this
value of the enclosing lexical scope is used. Arrow functions are ill-suited as methods, and they cannot be used as constructors.
Quiz on arrow functions
Get hands-on with 1400+ tech skills courses.