Higher Order Functions in JavaScript

Original author: Sukhjinder Arora
  • Transfer
  • Tutorial
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 is First-Class Functions.





The material that we are publishing 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 application architecture 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:

functiongreeting() {
  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 - better create a special object for this.

In JavaScript, the functions can do the same thing that you can do with other types of entities, 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:

functionformalGreeting() {
  console.log("How are you?");
}
functioncasualGreeting() {
  console.log("What's up?");
}
functiongreet(type, greetFormal, greetCasual) {
  if(type === 'formal') {
    greetFormal();
  } elseif(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 one that takes a function as an argument or returns a function as an output value.

For example, built-in JavaScript functions Array.prototype.map, Array.prototype.filterand Array.prototype.reduceare 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 method map()creates a new array, calling, for processing 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 map()in takes three arguments: element(element), index(index), and array(array). Consider 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 get 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 method filter()creates, on the basis of the 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 method map(), three arguments element, indexand array.

Consider an example built in the same way as when considering a method map().

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 over 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 method reduce()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).

Callback accepts four parameters: accumulator(battery), currentValue(current value), currentIndex(current index), sourceArray(source array).

If a parameter is provided to the method initialValue, then, at the beginning of the method, it accumulatorwill be equal to this value, and the currentValuefirst element of the array being processed will be written in.

If the parameter is initialValuenot provided to the method, then the accumulatorfirst element of the array will be written in, and the currentValuesecond will be written in .

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 method reduce()without giving it an initial value.

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

Each time a callback is called with the transfer to it currentValue, that is, the next element of the array, its parameter accumulatorturns 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 a constant sum.

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

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

As you can see, using 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 there is no standard array method in JavaScript 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'];
functionmapForEach(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 have created a higher order function mapForEachthat accepts an array and a callback function fn. The function mapForEachpasses through the array in a loop and calls a callback fnat each iteration of this loop.

Callback fntakes the current string element of the array and returns the length of this element. What the function returns is fnused in the command newArray.push()and falls into the array that the function returns mapForEach(). This array will eventually be written to a constant lenArray.

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 two words 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 the 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?


Also popular now: