From Anonymous to Arrow Functions

JavaScript has three different ways to define a function.

A named function uses the function keyword followed by the name of the function. For example, the following code defines a function named sqr:

 function​ sqr(n) { ​return​ n * n; }

An anonymous function has the same structure, except it does not have a name—it’s anonymous. An anonymous function can be passed to another function as an argument or stored into a variable. For example, here’s an anonymous function that is stored into a variable named sqr:

 const​ sqr = ​function​(n) { ​return​ n * n; };

The third relatively new function form that JavaScript supports is an arrow function. An arrow (=>) separates the parameter list from the short body of the function. An arrow function, like an anonymous function, can also be passed as argument to another function or can be stored into a variable. For example, let’s rewrite the previous anonymous function as an arrow function:

 const​ sqr = (n) => n * n;

Arrow functions do not have a name and are a lot more concise than anonymous functions. Let’s discuss using arrow functions compared to anonymous functions.

Passing functions as arguments to other functions is a common practice in JavaScript. Here’s an example of passing a small anonymous function as an argument to the setTimeout() function:

 setTimeout(​function​() { console.log(​'greetings'​); }, 2000);

When run, this code will produce the following output after a 2-second delay:

 greetings

The call to setTimeout() is a bit cluttered. The feature of arrow functions was introduced to address situations like this—to make function passing short and code more expressive. Let’s change our code to use an arrow function:

 setTimeout(() => console.log(​'greetings'​), 2000);

There is less clutter and less noise, and the code is crisp.

The structure of a short, single-line, anonymous function is

function(parameter1, parameter2, ...) { return ...body... }

The structure of the corresponding arrow function is

(parameter1, parameter2, ...) => ...body...;

Arrow functions have a parenthesized parameter list, an arrow, =>, and either a single-line body or a compound multiline body surrounded by {}. The return keyword is implicit and should be omitted if the body is not surrounded by {}.

JavaScript permits us to deviate from that syntax slightly on special occasions; let’s explore a few of those.

Dropping the Parentheses

The parentheses around the parameter list are required if the arrow function has an empty parameter list or takes two or more parameters. However, the parentheses are optional if the function takes only one parameter.

For example, let’s pass to setTimeout() a function that takes one parameter:

 const​ greet = (subject) => console.log(​'Hello '​ + subject);
 
 setTimeout(greet.bind(​null​, ​'Earthling'​), 2000);

The code first shows that an arrow function, like an anonymous function, can be saved into a variable, such as greet. The arrow function has a parenthesized parameter list with one parameter named subject, followed by the arrow and the single-line body. In the call to setTimeout(), we bind the value Earthling to the parameter using the good old bind() function.

Since our arrow function takes only one parameter, we can drop the parentheses, like so:

 const​ greet = subject => console.log(​'Hello '​ + subject);

If your team prefers consistency, use the () around the single parameter. If they prefer less clutter, drop the parentheses where possible.

Multiline Arrow Functions

Arrow functions are not restricted to having a single-line body. JavaScript permits arrow functions to be multiline.

Multiline arrow functions are not as crisp, concise, and expressive as single-line arrow functions. The multiple lines in the body should be enclosed within {}, each statement or expression is expected to end with ;, and if the body intends to return a value, then the return keyword is required. Here’s a piece of code that computes a factorial, defined as an anonymous function:

 const​ factorial = ​function​(number) {
 let​ product = 1;
 
 for​(​let​ i = 1; i <= number; i++) {
  product *= i;
  }
 
 return​ product;
 };

We may write this function as a multiline arrow function if we desire, like so:

 const​ factorial = (number) => {
 let​ product = 1;
 
 for​(​let​ i = 1; i <= number; i++) {
  product *= i;
  }
 
 return​ product;
 };

Compared to writing as an anonymous function, we merely replaced

 function(number)

with

 (number) =>

There was hardly any benefit, other than less typing. Arrow functions have less noise and are concise and expressive when compared to regular and anonymous functions, but that’s true only when the body is single line. Multiline arrow functions don’t have those benefits. In appearance, the body of a multiline arrow function is no different from the body of the corresponding regular/anonymous function, but semantic differences do exist.

Since multiline arrow functions don’t offer any syntactical benefit over regular/anonymous functions, should we ever choose them? Certainly we should not choose them to appear more fashionable. The decision to pick one or the other should be based on the semantic benefits that arrow functions offer compared to anonymous functions—we will discuss these in Anonymous vs. Arrow Functions. At the same time, be mindful of the limitations of arrow functions when making that decision—see Limitations of Arrow Functions.

Resist the urge to pass multiline arrow functions as function arguments—this makes the code hard to read. Consider the following example where the use of multiline arrow functions, I argue, is smelly.

 navigator.geolocation.getCurrentPosition(
  (position) => {
 const​ latitude = position.coords.latitude;
 const​ longitude = position.coords.longitude;
 
  document.getElementById(​'location'​).innherHTML =
 `​${latitude}​, ​${longitude}​`​;
  },
  (error) => {
  document.getElementById(​'location'​).innherHTML =
 `Error: ​${error}​`​;
  });

Compare that to the following call:

 navigator.geolocation.getCurrentPosition(onSuccess, onError);

It’s a lot easier to see what we’re passing to the getCurrentPosition() function: two arguments, one to deal with the success scenario and the other with the error scenario. The variables onSuccess and onError may refer to a regular function, an anonymous function, or a multiline arrow function, but must be defined before the call to getCurrentPosition(). This removes the clutter in code and makes the code expressive, easier to understand, and easier to maintain.

Using the Rest Parameter

In Using the Rest Parameter, we saw how functions can take a rest parameter. Arrow functions’ parameters can also have a rest parameter. Let’s create another version of the greet() arrow function that takes multiple subjects.

 const​ greet =
  (message, ...subjects) => console.log(message + ​' '​+ subjects.join(​', '​));
 
 greet(​'Hi'​, ​'Developers'​, ​'Geeks'​);

The function takes two parameters, message and subjects, where subjects is a rest parameter. The caller may pass a value for message and zero or more values for the second parameter. Here’s the output of directly calling the arrow function:

 Hi Developers, Geeks

In this example, since we had two arguments, we had to use the parentheses. Even if we had only one parameter, the relaxed rule for parentheses for a single parameter does not hold if it is a rest parameter. We have to use () if we use a rest parameter.

Using Default Arguments

In Defining Default Values for Parameters, we saw how functions can take default arguments. Arrow functions can also take default arguments. Here’s a short function that takes two parameters, the second with a default value.

 const​ power = (number, exp = 2) => Math.pow(number, exp);
 
 console.log(power(4, 3));
 console.log(power(4));

We can call the power() function with two arguments, as in the first call, or leave out the second argument, as in the second call. The default value kicks in when the argument is omitted, using the same rules for default arguments here with arrow functions as with regular functions. The output of this code confirms that behavior:

 64
 16

Again in this case, the parentheses are required when default arguments are involved, even if there is only one parameter.

We saw how arrow functions are useful to reduce clutter for short functions. They reduce noise where anonymous functions are passed as arguments. Before looking at some benefits of using arrow functions, let’s discuss some semantic differences between arrow functions and traditional functions.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset