In JavaScript, functions are treated as first-class objects. That means they can be stored as any other values in objects or arrays, passed around as arguments, or returned from other functions.
Higher-order functions are a functional programming pattern when functions are being passed as arguments to other functions or returned as a result.
The example below illustrates the HoF pattern when one function takes another function as an argument and returns composed data:
function log(item) {
return console.log(item);
}
function process(data, callback) {
for (let i = 0; i < data.length; i += 1) {
callback(data[i]);
}
}
process([1, 2, 3], log); // prints 1; 2; 3;
Function process
here is higher-order, it takes data
array, loops through all items and calls log
function on all of them.
Array HoF
The best example of HoF are methods, built-in in Array
object. If you are confused about name methods, they are simply functions stored in an object as properties. And in JavaScript, everything is an object, including arrays.
The most common used array HoF are:
- forEach()
- map()
- filter()
- reduce()
Let's take Array.prototype.map()
method as an example.
The method returns a new array with the result, populated by calling a function on each element of the array. Tham means map()
function takes another function (callback) as an argument and runs it on each item of the array.
const numbers = [1, 2, 3];
// pass unonymouse function
numbers.map(function(item) {
return item * 2;
}); // [2, 4, 6]
// or extract the callback into a named function
function double(item) {
return item * 2;
}
numbers.map(double); // [2, 4, 6]
Why It Is Useful?
The best part of higher-order functions is composability. It gives you the ability to combine functions and operate them in a sequence. For example, you could compose HoF in a pipeline with array methods:
const numbers = [1, 2, 3];
numbers
.map((n) => n * 2) // it will return [2, 4, 6]
.filter((n) => n % 4) // it will filter out number that divides by 4
.reduce((a, b) => a + b); // return 6 - sum of the array items
Or you could write your own HoF that takes any amount of callback functions and runs against the data:
function compose(...fns) {
return function(arr) {
return fns.reduceRight((acc, fn) => fn(acc), arr);
}
}
function pow2(arr) {
return arr.map(v => v * v)
}
function filterEven(arr) {
return arr.filter(v => v % 2);
}
const pipe = compose(filterEven, pow2);
pipe([1, 2, 3, 4]) // [1, 9];
The function compose
takes two functions: pow2
and filterEven
and returns an anonymous function that reduces passed data (an array of numbers) by applying callback functions from right to left. This way you can pass any number of callbacks, create different pipeline structures and pass various data.
As callbacks are pure functions (returns the same output, given the same input) they are easier to test, which reduces the amount of bugs and side effects.
Conclusion
We learned that functions are values and it defines how we are treating them in JavaScript. While the higher-order concept describes how we are using them. Most of the Array methods are HoF, as they take other functions as arguments. This concept lets to compose functions into pipelines, which makes code to read easier and less buggy.
Top comments (0)