📜 ⬆️ ⬇️

Higher Order Functions in JavaScript

If you are studying JavaScript, then you must have come across the concept of “Higher-Order Function”. It may seem that this is something very complicated, but, in fact, it is not.

JavaScript is suitable for functional programming due to the fact that it supports the concept of higher order functions. Such functions are widely used in the language, and if you programmed in JS, then you probably already worked with them without even knowing about it.


')
In order to fully understand this concept, you first need to understand the concept of functional programming (Functional Programming) and what are first-class functions.

The material we are translating is intended for beginners, it is aimed at explaining the concept of higher order functions, and at demonstrating how to use them in JavaScript.

What is functional programming?


If you describe the concept of functional programming in simple words, it turns out that this is an approach to programming, using which you can transfer functions to other functions as parameters and use functions as values ​​returned by other functions. Being engaged in functional programming, we design the architecture of the application and write code using functions.

Among the languages ​​that support functional programming, there are JavaScript, Haskell, Clojure, Scala and Erlang.

First class functions


If you are learning JavaScript, you might hear that in the language functions are treated as first-class objects. This is due to the fact that in JavaScript, as in other languages ​​that support functional programming, functions are objects.

In particular, in JS, functions are represented as objects of a special type — these are objects of type Function . Consider an example:

 function greeting() { console.log('Hello World'); } //   greeting();  //  'Hello World' 

In order to prove that functions in JavaScript are objects, we can do the following by continuing the previous example:

 //     ,       greeting.lang = 'English'; //  'English' console.log(greeting.lang); 

Note that although adding custom properties to standard objects in JavaScript does not cause error messages, it is not recommended. Do not add your own properties to the functions. If you need to store something in the object, it is better to create a special object for this.

In JavaScript, with functions, you can do the same thing that you can do with entities of other types, such as Object , String , Number . Functions can be passed as parameters to other functions. Such functions transferred to others usually act as callback functions (callbacks). Functions can be assigned to variables, stored in arrays, and so on. That is why functions in JS are first class objects.

Assigning Functions to Variables and Constants


Functions can be assigned to variables and constants:

 const square = function(x) { return x * x; } //   25 square(5); 

Functions assigned to variables or constants can be assigned to other variables or constants:

 const foo = square; //  36 foo(6); 

Transfer functions as parameters


Functions can be passed as parameters for other functions:

 function formalGreeting() { console.log("How are you?"); } function casualGreeting() { console.log("What's up?"); } function greet(type, greetFormal, greetCasual) { if(type === 'formal') {   greetFormal(); } else if(type === 'casual') {   greetCasual(); } } //  'What's up?' greet('casual', formalGreeting, casualGreeting); 

Now that we know how first-class functions behave, let's talk about higher-order functions.

Higher order functions


Higher order functions are functions that work with other functions, either taking them as parameters or returning them. Simply put, a higher order function is a function that takes a function as an argument or returns a function as an output value.

For example, the built-in JavaScript functions Array.prototype.map , Array.prototype.filter and Array.prototype.reduce are functions of a higher order.

Higher order functions in action


Consider examples of using higher-order functions built into JS and compare this approach with performing similar actions without using such functions.

ArArray.prototype.map method


The map() method creates a new array, calling, to process each element of the input array, a callback passed to it as an argument. This method takes each value returned by the callback and places it in the output array.

The callback function passed to map() takes three arguments: element (element), index (index), and array (array). Consider the examples.

Example №1


Suppose we have an array of numbers, and we want to create a new array that contains the results of multiplying these numbers by 2. Consider ways to solve this problem using and without higher-order functions.

Solving a problem without using higher order functions


 const arr1 = [1, 2, 3]; const arr2 = []; for(let i = 0; i < arr1.length; i++) { arr2.push(arr1[i] * 2); } //  [ 2, 4, 6 ] console.log(arr2); 

Solving a problem using the higher order map function


 const arr1 = [1, 2, 3]; const arr2 = arr1.map(function(item) { return item * 2; }); console.log(arr2); 

The volume of this code can even be reduced if we use the arrow function:

 const arr1 = [1, 2, 3]; const arr2 = arr1.map(item => item * 2); console.log(arr2); 

Example 2


Suppose we have an array containing the year of birth of some people, and we need to create an array that will reach their age in 2018. Consider, as before, the solution of this problem in two versions.

Solving a problem without using higher order functions


 const birthYear = [1975, 1997, 2002, 1995, 1985]; const ages = []; for(let i = 0; i < birthYear.length; i++) { let age = 2018 - birthYear[i]; ages.push(age); } //  [ 43, 21, 16, 23, 33 ] console.log(ages); 

Solving a problem using the higher order map function


 const birthYear = [1975, 1997, 2002, 1995, 1985]; const ages = birthYear.map(year => 2018 - year); //  [ 43, 21, 16, 23, 33 ] console.log(ages); 

ArArray.prototype.filter method


The filter() method creates, on the basis of an array, a new array in which the elements of the original array fall within the conditions specified in the callback function passed to this method. This function accepts, as is the case with the map() method, 3 arguments: element , index and array .

Consider an example built along the same lines as when considering the map() method.

Example


Suppose we have an array containing objects whose properties contain information about the name and age of representatives of a certain group of people. We need to create an array in which there will be information only about adult members of this group (those who are 18 years old).

Solving a problem without using higher order functions


 const persons = [ { name: 'Peter', age: 16 }, { name: 'Mark', age: 18 }, { name: 'John', age: 27 }, { name: 'Jane', age: 14 }, { name: 'Tony', age: 24}, ]; const fullAge = []; for(let i = 0; i < persons.length; i++) { if(persons[i].age >= 18) {   fullAge.push(persons[i]); } } console.log(fullAge); 

Solving a problem with a higher order function filter


 const persons = [ { name: 'Peter', age: 16 }, { name: 'Mark', age: 18 }, { name: 'John', age: 27 }, { name: 'Jane', age: 14 }, { name: 'Tony', age: 24}, ]; const fullAge = persons.filter(person => person.age >= 18); console.log(fullAge); 

ArArray.prototype.reduce method


The reduce() method processes each element of the array with a callback and places the result in a single output value. This method takes two parameters: a callback and an optional initial value ( initialValue ).

The callback takes four parameters: accumulator , currentValue (current value), currentIndex (current index), sourceArray (source array).

If the initialValue parameter is provided to the initialValue , then at the start of the method, the accumulator will be equal to this value, and the first element of the array being processed will be written to the currentValue .

If the initialValue parameter is not provided to the method, then the first element of the array will be written to the accumulator , and the second will be written to the currentValue .

Example


Suppose we have an array of numbers. We need to calculate the sum of its elements.

Solving a problem without using higher order functions


 const arr = [5, 7, 1, 8, 4]; let sum = 0; for(let i = 0; i < arr.length; i++) { sum = sum + arr[i]; } //  25 console.log(sum); 

Solving a problem with the higher order function reduce


First, consider using the reduce() method without providing it with an initial value.

 const arr = [5, 7, 1, 8, 4]; const sum = arr.reduce(function(accumulator, currentValue) { return accumulator + currentValue; }); //  25 console.log(sum); 

Each time a callback is called with passing currentValue , that is, the next element of the array, its accumulator parameter turns out to contain the results of the previous operation, that is, what was returned from the function at the previous iteration. After the completion of this method, the final result falls into the constant sum .

Now let's take a look at how the solution will look like if we pass the initial value to the reduce() method.

 const arr = [5, 7, 1, 8, 4]; const sum = arr.reduce(function(accumulator, currentValue) { return accumulator + currentValue; }, 10); //  35 console.log(sum); 

As you can see, the use of a higher order function has made our code cleaner, more concise and easier to read.

Creating your own higher order functions


So far, we have worked with higher-order functions built into JS. Now let's create our own function that works with other functions.

Imagine that in JavaScript there is no standard array method map() . We can easily create such a method on our own, which will be expressed in the development of a higher order function.

Suppose we have an array of strings, and we would like to create on its basis an array with numbers, each of which represents the length of a string stored in a certain element of the original array.

 const strArray = ['JavaScript', 'Python', 'PHP', 'Java', 'C']; function mapForEach(arr, fn) { const newArray = []; for(let i = 0; i < arr.length; i++) {   newArray.push(     fn(arr[i])   ); } return newArray; } const lenArray = mapForEach(strArray, function(item) { return item.length; }); //  [ 10, 6, 3, 4, 1 ] console.log(lenArray); 

In this example, we created a higher-order function mapForEach , which accepts an array and a callback function fn . The mapForEach function goes through the array in a loop and calls the fn callback at each iteration of this loop.

The callback fn takes the current string element of the array and returns the length of this element. What the fn function returns is used in the newArray.push() command and falls into an array returned by the mapForEach() function. This array will eventually be written to the lenArray constant.

Results


In this article, we talked about higher-order functions and explored some of the built-in JavaScript functions. In addition, we figured out how to create higher-order eigenfunctions.

If we express in a nutshell the essence of higher order functions, then we can say that these are functions that can take other functions as arguments and return other functions as results of their work. Working with other functions in higher order functions looks the same as working with any other objects.

Dear readers! Do you have to write higher-order eigenfunctions?

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


All Articles