Higher-Order Functions

Higher-order function are functions that operate on other functions, either by taking them as arguments or by returning them. Given that functions in JavaScript are regular values, there is nothing particularly remarkable about the fact that such functions exist.

The term comes from mathematics, where the distinction between functions and other values is taken more seriously.

Some Common Cases

We can pass an action to be repeated n times as a function value:

function repeat(n, action) {
  for (let i = 0; i < n; i++)
    action(i);
}

which could be called for instance like this:

repeat(3, console.log);

We don't have to pass a predefined function to repeat. Often, it is easier to create a function value on the spot instead

let labels = [];

repeat(5, i =>{
labels.push(`Unit ${i + 1}`);
});

console.log(labels);
// → ["Unit 1", "Unit 2", "Unit 3", "Unit 4", "Unit 5"]

We can have functions that create new functions.

function greaterThan(n) { return m => m > n;
}

let greaterThan10 = greaterThan(10);

console.log(greaterThan10(11));
// → true

We can also have functions that change other functions:

function noisy(f) {
  return (...args) => {
    console.log("calling with", args);
    let result = f(...args);
    console.log("called with", args, ", returned", result);
    return result;
  };
}

noisy(Math.min) (3, 2, 1);
// → calling with [3, 2, 1]
// → called with [3, 2, 1] , returned 1

We can even write functions that provide new types of control flow:

function unless(test, then) {
  if (!test) then();
}

repeat(3, n => {
  unless(n % 2 == 1, () => {
    console.log(n, "is even");
  });
});
// → 0 is even
// → 2 is even

On Functions Passed in as Arguments

These functions usually take three parameters:

Often enough, only the first argument is used (compulsory). The second and left are actually optional.

Filtering

Array.filter(FILTER_FUNCTION) is a built-in function that returns an array with only the elements that have passed the filter function. FILTER_FUNCTION is a function that takes an argument and returns either true or false.

Given an array (People) of persong objects, we could use Array.filter() like this to produce another array of elderly people:

console.log(People.filter(e => e.age >= 60));

Transforming with map()

Say we have an array of objects representing people, produced by filtering the people array somehow (those aged 60 or older, for instance). We want an array of personal names instead, which is easier to inspect.

The map method transforms an array by applying a function to all of its elements and building a new array from the returned values. The new array will have the same length as the input array, but its content will have been mapped to a new form by the function.

Like forEach and filter, map is a standard array method.


        


        


        
      

Summarizing with reduce()

Another common thing to do with arrays is to compute a single value from them. Our recurring example, summing a collection of numbers, is an instance of this. Another example is finding the eldest person in an array of people.

The higher-order operation that represents this pattern is called reduce (sometimes also called fold). It builds a value by repeatedly taking a single element from the array and combining it with the current value. When summing numbers, you'd start with the number zero and, for each element, add that to the sum.

reduce(callbackFn)
reduce(callbackFn, initialValue)

Note: If the first prototype is used, then the initial value is the first element in the array.


The full prototype of reduce() is:

array.reduce(function(total, currentValue, currentIndex, arr), initialValue = arr[0])

where

Parameter Description
total (required) The initialValue, or the previously returned value of the function
currentValue (required) the value of the current element
currentIndex (optional) the index of the current element
arr (optional) the array the current element belongs to

The parameters to reduce are, apart from the array, a combining function and a start value. This function is a little less straightforward than filter and map, so take a close look at a plausible implementation:

function reduce(array, combine, start) {
  let current = start;
  for (let element of array) {
    current = combine(current, element);
  }
  return current;
}

which we might apply like so:

console.log(reduce([1, 2, 3, 4], (a, b) => a +b, 0));
// → 10

There is indeed a standard array method Array.reduce, which of course corresponds to this function. Moreover, it has an added convenience: if your array contains at least one element, you are allowed to leave off the start argument. The method will take the first element of the array as its start value and start reducing at the second element.

An example of usage:

console.log([1, 2, 3, 4].reduce((a, b) => a + b));
// → 10

eval(TEXT)

You can execute JavaScript code with eval():

let x = 10;
let y = 20;
let text = "x * y";
let result = eval(text);

As a general rule, do not use eval(). Use code or a function instead:

let x = 10;
let y = 20;
let result = x * y;