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:
- value: the element in the array being processed,
- index: index of the first element into the array, and
- array: the array.
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;