Higher Order Functions you either love them or hate them. In case you don't know what these functions are or you are out of the loop, HOFs allow for reusable code blocks and an easier time writing code in large JavaScript files. Whether that be shaving some time recoding the same function or code block for different uses or using some built in HOFs, everyone can agree they are life savers (or finger savers). In this post, I'll go into more detail about what are HOFs and talk about a few built in HOFs. Without further ado, let's start explaining what HOFs are.
What are Higher Order Functions?
HOFs are just functions that take callback functions as arguments or functions that can return functions. To help understand what I mean, I will provide a function that uses callback function as an argument.
// a simple function called add to be used as callback
function add(x, y) {
return x + y;
}
// an add function that will take in a callback
function strangeAdd(x, y, callback) {
return callback(x, y) + callback(x, y);
}
let answer = strangeAdd(10, 20, add);
console.log(answer); // answer will equal 60
The strangeAdd function takes in a callback function, in this case the add function, and uses the function in its execution. There is another example of HOFs, and that is a function that returns a function. I will now provide an example now.
// a function that will return a math based function based on callback
function strangeMath(callback) {
let math = "Math rules";
return function(x, y) {
console.log(math);
return callback(callback(x, y), callback(x, y));
}
}
With this strangeMath function we are able to make any math based function like add or multiplication.
const strangeAdd2 = strangeMath(add) // a second strange add function
const strangeMult = strangeMath((x, y) => x * y) // a strange multiply function
let answer2 = strangeAdd2(30, 40);
let answer3 = strangeMult(3, 3);
console.log(answer2); // gives 140, "Math rules"
console.log(answer3); // gives 81, "Math rules"
You might notice that besides the answer being logged, the phrase "Math rules" is also being given. Why is this happening? Closure is why! To explain what closure is, I will reference this line from MDN web docs' article called "Closures", "A closure is the combination of a function bundled together (enclosed) with references to its surrounding state (the lexical environment)." To basically summarize this line, this allows the returning function to access and use variables previously declared in the function it is being returned from. This allows our strangeMult, or strangeAdd2, the ability to access the math variable in strangeMath and be able to log it to the console when the function is called.
Pretty simple right? Closure also works the same way for nested returned functions. An example is being shown.
// a function chain that returns the sum of 3 separate arguments
function deepAdd(a) {
return function(b) {
return function(c){
return a + b + c;
}}}
console.log(deepAdd(10)(20)(30)); // returns 60
This should give you the basics of what HOFs are. But you may recall that I said something about some built in HOFs. Let's get on with that.
Some built in HOFs
There are many built in HOFs so I won't be able to go over all of them, but I can tell you about the main ones you will use a lot while coding.
.map()
First and foremost is .map(), use this on an array to return a new array that has the callback function applied to every element. An example of using map would be:
const numbers = [1, 2, 3, 4]; // array of numbers
let times2 = numbers.map(function(num) {
return num * 2;
})
console.log(times2); // gives [2, 4, 6, 8]
In this example, map applies the anonymous function to every element in the numbers array and returns the result into the new function, time2.
.filter()
Another one you might often use is .filter(). Filter is also used on an array and returns a new array, but it filters out values that do not pass a truth test. Example as follows:
const values = [10, 11, 12, 13, 14];
let even = values.filter(function(val) {
return val % 2 === 0;
})
console.log(even); // [10, 12, 14]
In the example above, we have an array called values that has values 10 - 14 and we only want the even numbers. So we declared a variable called even which is set to the output of the .filter function on values. The function inside of .filter checks for numbers that have a remainder of 0 when divided by 2. If it does have a remainder of zero, that element goes into the new array.
.reduce()
The third and final HOF that I want to talk about, .reduce().
Reduce is a bit complicated, but once you understand reduce, you will be able to use it for a majority of your code, even where you might use .map, .filter, or any other HOF. In the previous examples, we did something like this (array.HOF(functionToCall(parameter))) in order to use map and filter. Reduce has another parameter to account for, a 'seed' value and the functionToCall needs at least two parameters (accumulator and current) in order to work properly (array.reduce(functionToCall(accumulator, current, index), /seed/). The seed can either be provided or not provided, if it is, it will change the way the reduce works. Without the seed, it will iterate over the array with the accumulator as the first value of the array, this allows for either comparing values against each other.
// comparing values
const array = [100, 3, 103, 200];
let highest = array.reduce(function(high, num) {
if (high > num) {
return high;
} else {
return num;
}
});
console.log(highest); // 200 is given
I have created an array of numbers in a random order. Then I have created a variable called highest which will equal to the result of the reduce function. The callback function takes two parameters, high which equals the highest number and num which equals the current number. Reduce automatically sets high to the first index, given the fact that no seed value was provided. Then the conditional checks to see if high is the largest number, if it is, keep it and move on to the next number, if not, the value of num is returned which becomes the new high value. After it is done iterating over the whole array, it returns the highest value, which is 200 in this case. Now to show the power of the seed value. The seed can be any value from strings and numbers to arrays and objects. For demonstration purposes, I will use an array in this example.
const array2 = [12, 30, 43, 89];
let times5 = array2.reduce(function(arr, num){
arr.push(num * 5);
return arr;
}, []);
console.log(times5); // gives [60,150,215,445]
I have created another array that has numbers in ascending order. Then I declare and assign a variable called times5 and set that to the output of reduce on array2. The callback function will push the elements into the arr parameter given that the seed value is an empty array, accumulator becomes seed value. When it does push the num, it multiplies it by 5 before pushing it. You should now have a decent understanding of how .reduce(), .map(), and .filter() works now.
Key Take-aways/Conclusion
Higher Order Functions are a great tool to use in your coding repertoire. HOFs are just functions that use other functions as arguments. HOFs are also capable of returning other functions and those functions use closure to access variables outside of their scope. There are also built-in HOFS that you don't have to code in order to use them. So go ahead, try implementing some higher order functions in your code, it will save you a lot of time when you are in a time crunch.
If you want to learn more about HOFs, check out these articles:
Joan Ayebola on freeCodeCamp: What are Higher Order Functions in JavaScript? Explained With Examples
GeeksforGeeks article: JavaScript Higher Order Functions
Sobit Prasad on freeCodeCamp: Higher Order Functions in JavaScript – Explained with Practical Examples
Top comments (0)