With the release of the ES6 standard in 2015, programmers have gotten several new features and functions in JavaScript. The new standard added arrow functions, ways to declare let
and const
variables, promises, and many other fundamental changes to the language. Today, we will focus on arrow functions.
Arrow functions in JavaScript are a new method of declaring functional expressions that is much more compact and convenient than its predecessor. If you are familiar with lambda functions in Python, you will definitely find similarities between them.
In this article, we'll look at arrow functions and their syntax, give usage examples, and learn the ins and outs of using them in code.
-
Before we study arrow functions, we should revise the traditional way of declaring functions and function expressions. This chapter will help users to remember what functions and function expressions are and to apprehend how their declaration differs from the arrow functions.
A function is a set of commands written to accomplish a specific task when called. It can be called from any part of the code as many times as you like.
The syntax of a standard function declaration is as follows:
function function_name(set_of_parameters) {
set_of_commands;
}
A function declaration is always read first, meaning it can be called before it is declared. This process is called hoisting.
An example of an ordinary function:
console.log(multiplication(2,5));
function multiplication (x, y) {
return x * y;
}
A function definition expression is another method of declaring a function by assigning it to a variable or constant. It can then be passed as an argument to another function or used as a return value.
The syntax for declaring a function expression is as follows:
var variable_name = function function_name (parameters) {
commands;
}
The function_name
parameter is optional. In its absence, the function will be considered anonymous.
Unlike regular functions, a functional expression can only be called after it has been declared in the code.
An example of a functional expression:
const multiplication = function (x, y) {
return x * y;
}
console.log(multiplication(2,5));
Arrow functions (arrow function expressions) are the same as function expressions but anonymous and have some syntax peculiarities. Let's take a closer look at those peculiarities.
Concise and clear syntax
The syntax of arrow functions in JavaScript is intuitive and concise. Its basic version is as follows:
(set_of_parameters) => {
set_of_commands;
}
As we can see, the main difference between arrow functions and normal functions is the absence of the function
keyword and the addition of =>
characters after the argument list in brackets.
For example:
const multiplication = (x, y) => x * y;
console.log(multiplication(2,5));
Here, we did not use curly braces because the function's body contains only one operation. Also, we did not specify an explicit return of the result, as it will happen automatically.
When we execute this code, the result is as follows.
The this
keyword
Arrow functions operate only in the scope in which they are declared, and do not have their own functional execution context. This implies that entities such as this
or argument
are inherited exclusively from parent functions.
For a more detailed understanding, here is an example where we will use both a regular function and an arrow function:
function Animal() {
this.group = 'Mammals',
this.name = 'Elephant',
this.Animal1 = function () {
console.log('Operation of a normal function:')
console.log(this.group);
function exampleFunction1() {
console.log(this.name);
}
exampleFunction1();
}
this.Animal2 = function () {
console.log('Operation of an arrow function:')
console.log(this.group);
let exampleFunction2 = () => console.log(this.name);
exampleFunction2();
}
}
let x = new Animal();
x.Animal1();
let y = new Animal();
y.Animal2();
In this example, this.group
is available both inside this.Animal1
and inside this.Animal2
, since these are two methods of the same object. However, inside these methods, in the first case this.name
returns an undefined value, and the output is empty since this function has its own context (in this case, window
). In the second case, however, it references the parent scope and returns this.name = 'Elephant'
.
When we execute the code, we receive the following output:
Operation of a normal function:
Mammals
Operation of an arrow function:
Mammals
Elephant
Constructors
Arrow functions cannot be used as constructors, nor can they have their own properties and methods because they lack the prototype
property the normal functions have. When calling them with the new
operator, the system will generate an error.
Also, since the arrow is followed by curly braces denoting a code block with commands, the user will not be able to create an object inside the function or return an object literal from it in the usual way. For example, the following code fragment will generate an error:
(firstName, lastName) => {firstName: firstName, lastName: lastName};
The system considers it the function's body; that's why an error occurs in this code fragment. To fix this, we need to enclose the object literal in parentheses:
(firstName, lastName) => ({firstName: firstName, lastName: lastName});
Arrow functions are acceptable anywhere in a program but are especially useful in callback functions that take other functions as parameters. In the following example, we use it as a callback for the map
method:
const example_numbers = [1, 2, 3, 4, 5];
const doubling = example_numbers.map(number => number * 2);
console.log(doubling);
Like with curly braces, we can omit parentheses if only one parameter is specified, as in the example above.
Another example of arrow functions is using them in the reduce
method. In the following example, we use an arrow function to add up all the elements of an array:
const example_numbers = [1, 2, 3, 4, 5];
const sum = example_numbers.reduce((total, number) => total + number, 0);
console.log(sum);
As a result, we get the following:
There are cases where regular functions are preferable to arrow functions.
Mostly, they are related to the this
keyword behavior and its peculiarities, because of which you shouldn't use arrow functions in object methods.
Let’s look at this example:
let animal = {
group: 'Amphibians',
name: 'Lake frog',
infoAnimal: () => {
console.log('Animal group -',this.group);
console.log('Animal name -',this.name);
},
}
animal.infoAnimal();
Here, we have the infoAnimal
method, which outputs information about the object when we call it with the animal.infoAnimal()
. In this case, its lexical environment is window
. Hence, it has nothing to do with the group
and name object
properties.
This is the result we get:
Animal group - undefined
Animal name - undefined
Now let's rewrite the code using a normal function and look at the result:
let animal = {
group: 'Amphibians',
name: 'Lake frog',
infoAnimal: function () {
console.log('Animal group -',this.group);
console.log('Animal name -',this.name);
},
}
animal.infoAnimal();
We can see that this method works successfully:
Animal group - Amphibians
Animal name - Lake frog
In addition to object methods, you shouldn't use the arrow functions for functions with a dynamic context, because they bind the context statically. For example, this applies to working with event handlers or event listeners.
Arrow functions (arrow function expressions) are the same as function expressions, but anonymous and with some syntax peculiarities.
Their basic syntax is as follows:
(set_parameters) => {
set_commands;
}
When using the arrow functions, you can remove parentheses around a parameter if it is a single parameter. If the function implements only one operation, you can also use implicit return, which means deleting curly brackets and the return
keyword.
Arrow functions work only in the scope in which they were declared, and do not have their own functional execution context. So, entities such as this
or argument
are inherited exclusively from parent functions.
When working with object methods, dynamic context or constructors, you should use regular functions, not arrow functions.