5 tips for writing quality arrow functions

JavaScript arrow functions are pretty popular. And they deserve it, differing in laconic syntax, lexical binding thisand the fact that they are very convenient to use as callbacks. The material, the translation of which we publish today, includes 5 recommendations on the use of arrow functions. The one who takes advantage of these recommendations has a chance to more fully reveal the potential of such functions.





1. Use the display of arrow function names


JavaScript arrow functions are anonymous: their property namecontains an empty string - ''.

( number => number + 1 ).name; // => ''

Anonymous functions are marked as anonymousduring debugging or when analyzing the call stack. Unfortunately, the word anonymousdoes not contain a hint about which specific code it relates to.

Here is one of the points in debugging code where anonymous functions are called.


Debugging code containing anonymous functions

The call stack on the right side of the figure contains references to 2 functions marked asanonymous. From such information contained in the call stack, one cannot learn anything useful about the code.

Fortunately, JavaScript has a mechanism for deriving function names (ES2015 feature), which is capable, under certain conditions, of detecting a function name. The idea of โ€‹โ€‹the name output is that JavaScript can figure out the name of the arrow function from its syntactic position, that is, based on the name of the variable that stores the link to the function object.

Let's look at the mechanism for deriving function names in action:

const increaseNumber = number => number + 1;

increaseNumber.name; // => 'increaseNumber'

Since the reference to the arrow function is stored in a variable increaseNumber, JavaScript decides that the name is 'increaseNumber'well suited for the function. As a result, the arrow function also gets such a name.

It is recommended to use the function name derivation mechanism for naming arrow functions.

Now let's look at the debugging process, during which the mechanism for deriving function names is used.


Debugging code containing arrow functions that are given names

Since arrow functions now have names, the call stack provides more useful information about the code being executed:

  • The name of the function handleButtonClickindicates a call to the event handler click.
  • The name increaseCounterindicates a call to the function that increments the counter.

2. Whenever possible, strive to write inline functions


An โ€œinlineโ€ function is a function consisting of a single expression. In arrow functions, I like the fact that they are very convenient to use for creating short inline functions.

For example, here is the โ€œfullโ€ form of an arrow function:

const array = [1, 2, 3];

array.map((number) => { 
  return number * 2;
});

This feature can easily be made more compact. Namely, since the function contains only one expression, you can remove curly braces and return:

const array = [1, 2, 3];

array.map(number => number * 2);

It is recommended that if a function contains only one expression, make it inline.

3. Carefully use arrow functions and comparison operators.


Comparison operators >, <, <=and >=are very similar to the arrow =>by which declare arrow functions (such arrows is also called "a bold arrow").

When comparison operators are used in an inline function, the resulting code may not be very clear.

Declare an arrow function that uses the operator <=:

const negativeToZero = number => number <= 0 ? 0 : number;

The presence in one line of characters =>and <=disorientates the code reader.

In order to clearly distinguish arrows from comparison operators, several approaches can be used.

First, you can enclose the expression in parentheses:

const negativeToZero = number => (number <= 0 ? 0 : number);

Secondly, you can intentionally declare an arrow function using a longer construction:

const negativeToZero = number => {
  return number <= 0 ? 0 : number;
};

These transformations remove the uncertainty caused by the use of both the arrow symbol and comparison operators in one line.

It is recommended that if one-line function An arrow contains statements >, <, <=and >=, to conclude the corresponding expression in parentheses or use a multi-form ads arrow functions.

4. Use parentheses or multi-line structures when creating simple objects in arrow functions


Using an object literal inside an embedded arrow function will result in a syntax error:

const array = [1, 2, 3];

//  SyntaxError!
array.map(number => { 'number': number });

JavaScript considers curly braces to be a block of code, not an object literal.

If you enclose the object literal in parentheses, this problem will be solved:

const array = [1, 2, 3];

// !
array.map(number => ({ 'number': number }));

If an object literal has many properties, then here you can even use line feeds. The arrow function still remains embedded:

const array = [1, 2, 3];

// !
array.map(number => ({
  'number': number
  'propA': 'value A',
  'propB': 'value B'
}));

Object literals, when used in inline functions, are recommended to be enclosed in parentheses.

5. Be careful to nest too deeply.


Arrow functions differ in laconic syntax. It's good. But because of this, many arrow functions embedded in each other can form difficult to read structures.

Consider the following scenario. When they click on the button, a request to the server is executed. When a server response containing a set of certain elements is received, the names of these elements are displayed in the console:

myButton.addEventListener('click', () => {
  fetch('/items.json')
    .then(response => response.json())
    .then(json => {
      json.forEach(item => {
        console.log(item.name);
      });
    });
});

Here we see three levels of nesting arrow functions. In order to delve into what is happening in this code, you need to spend some time.

Several approaches can be used to improve the readability of nested functions.

The first approach is to write function references to variables. Variable names should briefly describe the essence of the function (take a look at recommendation # 1 on the derivation of function names).

const readItemsJson = json => {
  json.forEach(item => console.log(item.name));
};

const handleButtonClick = () => {
  fetch('/items.json')
    .then(response => response.json());
    .then(readItemsJson);
};

myButton.addEventListener('click', handleButtonClick);

During refactoring, we extracted nested arrow functions and wrote them into the readItemsJsonand variables handleButtonClick. The nesting level of the code decreased from 3 to 2. Now the code has become more understandable.

Another option for refactoring this code is to translate the entire function into a format async/await. This is a great way to solve the problem of nested functions:

const handleButtonClick = async () => {
  const response = await fetch('/items.json');
  const json = await response.json();
  json.forEach(item => console.log(item.name));
};

myButton.addEventListener('click', handleButtonClick);

It is recommended to avoid too deep nesting levels of arrow functions, extracting them into variables as separate functions, or, if possible, using syntax async/await.

Summary


JavaScript arrow functions are anonymous. In order to make code debugging more productive, it is recommended to use variables that store function references. This allows JavaScript to display function names.

Embedded arrow functions are useful when the function body contains only one expression.

Operators >, <, <=and >=like an arrow =>that is used when declaring the function switches. When these operators are used in the body of an embeddable arrow function, you should consider code conversion.

The syntax of object literals, like { prop: 'value' }, is like a block of code {}. As a result, when an object literal is placed inside an embedded arrow function, it must be enclosed in parentheses:() => ({ prop: 'value' }).

Too high levels of nesting of functions confuse the one who reads the code. It is recommended to reduce the nesting level of functions by extracting them and writing them into variables. Another approach to reducing the level of code nesting is to use a construct async/await.

Dear readers! Do you know any interesting tricks for using arrow functions?

Source: https://habr.com/ru/post/undefined/


All Articles