Hello Sunil
higher-order-functions-javaScript

Higher Order Functions in JavaScript – Beginner’s Guide

As a frontend developer, you should always strive to learn new techniques and discover ways to work smarter with JavaScript. One such technique is using higher order functions.

Higher order functions are one of the topics that can be hard to understand. This article will help you understand what higher order functions are and how to work with them.

You will also learn about the difference between high order and first class functions and about high order built-in functions in JavaScript.

Functions are first-class citizens in JavaScript

In JavaScript, functions are first-class citizens(JavaScript treats functions as objects). This means that they can be assigned to a variable and/or be passed as arguments and can also be returned from another function.

These features or abilities open the door for various types of functions like, First-class functions, Callback functions, Higher-order functions, Anonymous functions and more.

Having a clear and better understanding of how higher order functions work can save you a lot of time and make your logical implementation seamless.

JavaScript and first class functions

First-class functions are a unique feature in JavaScript, meaning that functions here are treated like any other variable. They can be passed as arguments, returned by another function, or assigned as a value to a variable.

In JavaScript, everything you can do with other types like object, string, or number, you can do with functions. You can pass them as parameters to other functions (callbacks), assign them to variables and returned from another function. This is why functions in JavaScript are known as First-Class Functions.

Assigning functions to variables:

Assigning functions to variables is one common thing you can do with them. This is also called creating function expressions.

The reason for this name is that you create a function inside an expression. You then assign this expression to a variable. From now on you can use the variable name to reference, and call, this function.

// Create function with function expression and assign it to a variable
const myFunc = function() {
  return 'Hello'
}
console.log(myFunc()); //Hello

Passing functions as arguments:

One thing you may have seen is passing functions as arguments to other functions. These passed functions are usually passed as the last argument and later used as callbacks.

A callback is a function that gets executed when all operations that have been completed. This is a common practice.

This works due to the fact that JavaScript is single-threaded programming language. This means that only one operation can be executed at the time. So, when you pass a callback function, and invoke it at the end, it will be invoked when all preceding operations are completed.

One situation when functions are passed as arguments frequently is when you work with event listeners.

target.addEventListener(event, function, useCapture)

The addEventListener has three parameters. event and function are most frequently used. useCapture is used occasionally.

Also Read: AddEventListener in JavaScript – How To Use It ?

Also, the second parameter, the function, is the callback. When specific addEventListener is triggered this callback function is invoked.

<body>
	<button id='btn'>Hello</button>

	<script>
		// Find button in the DOM
		const button = document.querySelector('#btn')

		// Create function to handle the event
		function handleButtonClick() {
			// Show log on click
			console.log('click on: ', this)
		}

		// Create event listener for click event on button
		// and pass "handleButtonClick" function as a callback function
		button.addEventListener('click', handleButtonClick)

		//Output - click on:  <button id='btn'>Hello</button>
	</script>
</body>

Kindly note here we passed the handleButtonClick function by name, without parentheses. This means that you are passing the function object itself.

If you passed that function with parentheses that would mean you are invoking the function right away and passing the result of executing that function.

Lets see one more example:

function formalGreeting() {
  console.log("How are you?");
}
function casualGreeting() {
  console.log("What's up?");
}
function greet(type, greetFormal, greetCasual) {
  if(type === 'formal') {
    greetFormal();
  } else if(type === 'casual') {
    greetCasual();
  }
}
greet('casual', formalGreeting, casualGreeting);
// prints 'What's up?'

Returning functions:

Another thing you can do with functions is that you can return them from other functions. This is something expected, since functions are a type of objects, and you can return objects in JavaScript.

This can be useful when you want to use function to create templates for new functions.

// Create function that returns function
// a template for new functions
function changeText(word, replacement, text) {
  // Return a new function
  // that will be later assigned to a variable
  return function(text) {
    return text.replace(word, replacement)
  }
}

// Create new function for changing text
// to positive mood using the "changeText" function
// This invokes and "changeText" function
// and stores returned function in a variable
const makePositive = changeText(/bad/g, 'good')

// Create new function for changing text
// to negative mood using the "changeText" function
// This invokes and "changeText" function
// and stores returned function in a variable
const makeNegative = changeText(/good/g, 'bad')

// Call the "makePositive" function
// This invokes the returned function
// stored in the variable after calling
// the "changeText" function
makePositive('Everything that happened is bad and everything that will happen is also bad.')
// 'Everything that happened is good and everything 
//that will happen is also good.'

// Call the "makePositive" function
// This invokes the returned function
// stored in the variable after calling
// the "changeText" function
makeNegative('Everything that happened is good and everything that will happen is also good.')
// 'Everything that happened is bad and everything 
//that will happen is also bad.'

Let’s see an example:

function calculate(operation) {
  switch (operation) {
    case "ADD":
      return function (a, b) {
        console.log(`${a} + ${b} = ${a + b}`);
      };
    case "SUBTRACT":
      return function (a, b) {
        console.log(`${a} - ${b} = ${a - b}`);
      };
  }
}
console.log(calculate("ADD")); // [Function (anonymous)]

In the above code, when we invoke the function calculate with an argument, it switches on that argument and then finally returns an anonymous function.

So if we call the function calculate() and store its result in a variable and console log it, we will get the following output:

function calculate(operation) {
  switch (operation) {
    case "ADD":
      return function (a, b) {
        console.log(`${a} + ${b} = ${a + b}`);
      };
    case "SUBTRACT":
      return function (a, b) {
        console.log(`${a} - ${b} = ${a - b}`);
      };
  }
}
const calculateAdd = calculate("ADD");
console.log(calculateAdd); // [Function (anonymous)]

You can see that calculateAdd contains an anonymous function that the calculate() function returned.

There are two ways to call this inner function which we’ll explore now.

Call the returned function using a variable:

In this method, we stored the return function in a variable as shown above and then invoked the variable to the inner function.

Let’s see it in code:

function calculate(operation) {
  switch (operation) {
    case "ADD":
      return function (a, b) {
        console.log(`${a} + ${b} = ${a + b}`);
      };
    case "SUBTRACT":
      return function (a, b) {
        console.log(`${a} - ${b} = ${a - b}`);
      };
  }
}
const calculateAdd = calculate("ADD");
calculateAdd(2, 3);
// Output: 2 + 3 = 5


const calculateSubtract = calculate("SUBTRACT");
calculateSubtract(2, 3);
// Output: 2 - 3 = -1

So what’d we do here?

  • We called the calculate() function and passed ADD as the argument
  • We stored the returned anonymous function in the calculateAdd variable, and
  • We invoked the inner returned function by calling calculateAdd() with the required arguments.

Call the returned function using double parentheses:

This is a very sophisticated way of calling the inner returned function. We use double parentheses ()() in this method.

Let’s see it in code:

function calculate(operation) {
  switch (operation) {
    case "ADD":
      return function (a, b) {
        console.log(`${a} + ${b} = ${a + b}`);
      };
    case "SUBTRACT":
      return function (a, b) {
        console.log(`${a} - ${b} = ${a - b}`);
      };
  }
}
calculate("ADD")(2, 3);
// Output: 2 + 3 = 5

calculate("SUBTRACT")(2, 3);
// Output: 2 - 3 = -1

The arguments in the first parentheses belong to the outer function, while the arguments in the second parentheses belong to the inner returned function.

The calculate() method returns a function as explained earlier, and it is that returned function which is immediately called using the second parentheses.

What are Higher Order Functions 🧐?

You may wonder what those three things have to do with high order functions. The first one about variables not much. However, the second and third, passing functions as arguments and returning functions, a lot.

Here is the thing, high order functions are functions that take another function as an argument, and/or returns another function.

So an Higher Order Function is a function that will at least :

  • take one or more functions as arguments
  • returns a function as its result

Higher order functions are functions that operate on other functions, either by taking them as arguments or by returning them. In simple words, A higher order function is a function that receives a function as an argument or returns the function as output.

The function that is passed as an argument to the higher-order function is known as a Callback Function, because is it, to be ‘called-back’ by the higher order function at a later time.

In all examples, where you were passing a function as an argument, or returning a function, you were actually working with high order functions.

You probably expected something more complex than this. Especially due to how many JavaScript developers talk about high order functions. However, it is really that simple.

// High-order function no.1:
// Function that takes a function as a argument
function myHighOrderFuncOne(myFunc) {
  // some code
}

// High-order function no.2:
// Function that returns a function
function myHighOrderFuncTwo() {
  // some code

  // Return a function
  return function() {
    // some code
  }
}

// High-order function no.3:
// Function that takes a function as a argument
// and also returns a function
function myHighOrderFuncThree(myFunc) {
  // some code

  // Return a function
  return function() {
    // some code
  }
}

Few more examples are as follows:

Taking a function as an argument:

Here is an example of a function that takes a function as an argument.

// normal function
function greetEnglish(name){
  return `Hello ${name}`;
}
function greetSpanish(name){
  return `Hola ${name}`;
}
// Our higher order function
function greeting(otherGreeting, name){
  console.log(`Hello ${name}`);
  return console.log(otherGreeting(name));
}
greeting(greetSpanish, "Sunil"); 
//output: Hello Sunil // first greets in English 
//output: Hola Sunil  // greets in the language passed 

In the above code snippet the two functions, greetEnglish(name)and greetSpanish(name) returns the greeting in respective language.

The function greeting() is a higher order function that takes another function and a string as a parameter. This function prints greeting in English by default and in any other language depending on the passed function as a parameter(here in Spanish).

Also, higher order function can also accept anonymous function as a parameter.

function greeting(callback, name){
  console.log(`Hello ${name}`);
  return callback(name);
}
greeting(function (name) {
    console.log(`hola ${name}`)
} , "Sunil")

//output: Hello Sunil /* first greets in English */
//output: Hola Sunil  /* second greets in Spanish */

We can also express the same code in arrow function way:

function greeting(callback, name){
  console.log(`Hello ${name}`);
  return callback(name);
}
greeting(name => {console.log(`hola ${name}`)}, "Sunil")

//output: Hello Sunil /* first greets in English */
//output: Hola Sunil  /* second greets in Spanish */

Also Read: Arrow Functions in JavaScript

Returning a function as a result:

Now let us take another function that returns a function.

// Define a function that returns a function
function returnFunc() {
  return function() {
    console.log('Hi');
  }
}

// Take the returned function in a variable.
const fn = returnFunc();

// Now invoke the returned function.
fn(); // logs 'Hi' in the console

// Alternatively - A bit odd syntax but good to know
returnFunc()(); // logs 'Hi' in the console

Both of the examples above are examples of Higher-Order functions. They either accept a function as an argument or return a function.

Please note, it is not mandatory for a higher order function to perform both accepting an argument and returning a function. Performing either will make the function a higher order function.

In-built Higher Order Functions in JavaScript

In JavaScript, there are plenty of usages of higher order functions. You may be using them without knowing them as higher order functions.

For example, take the popular Array methods like, map(), filter(), reduce(), find(), sort() and many more. All these functions take another function as an argument to apply it to the elements of an array.

In-built Higher Order Functions – map()

map() method allows us to process each element in the array and give back a new array with the same length.

var Array2 = [4,8,12,16,20];
var data = Array2.map(function(element){
  return element + 4; 
})
console.log(data); //[ 8, 12, 16, 20, 24 ]

data contains an array where each of the elements in Array2 gets added by 4. The array on which map is applied will produce an array with the same length.

In-built Higher Order Functions – filter()

filter() method allows to filter out some element in the array and give the rest of the element as an array output.

var Array4 = [12,45,41,74,91];
var data = Array4.filter(function(element){
   return element > 40; 
})
console.log(data); //[ 45, 41, 74, 91 ]

data contains all the elements present in Array4 which is more than 40.

In-built Higher Order Functions – reduce()

reduce() method allows us to process all the elements in the array to give back a single value.

var Array3 = [112,415,4,274,911];
var data = Array3.reduce(function(a,b){
   return a+b;
},0)
console.log(data); //1716

Here data contains the sum of all the elements present in the Array3. The start element in the syntax is required to have a start value before the reduction starts.

In the above example If we give 2 instead of 0 in the start value. 2 will be added to the sum of all elements in that array.

And If the array on which reduction is applied is having at least 1 element then we can miss out that start value in the function. The reduction will start from first element of the array then second element and so on.

In-built Higher Order Functions – forEach()

forEach() method allows us to loop over each element in an array.

var Array1 = [1,2,3,4,5];
var data = Array1.forEach(function(element){
   console.log(element);
})
console.log(data); 
//Output - 
//1
//2
//3
//4
//5

Here element represents each element in the Array1. It prints out all the elements present in the array. Kindly note, forEach() method acts same as that of a for loop.

Creating Our Own Higher Order Function in JavaScript

Up until this point, we saw various higher order functions built into the JavaScript language. Now let’s create our own higher order function.

Let’s imagine JavaScript didn’t have the native map method. We could build it ourselves thus creating our own higher order function.

Let’s say, we have an array of strings and we want to convert this array to an array of integers, where each element represent the length of the string in the original array.

const strArray = ['JavaScript', 'Python', 'PHP', 'Java', 'C'];
function mapForEach(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;
});

console.log(lenArray); // prints [ 10, 6, 3, 4, 1 ]

In the above example, we created an higher order function mapForEach which accepts an array and a callback function fn. This function loops over the provided array and calls the callback function fn inside the newArray.push function call on each iteration.

The callback function fn receives the current element of array and returns the length of that element, which is stored inside the newArray. After the for loop has finished, the newArray is returned and assigned to the lenArray.

Conclusion

We have learned what higher order functions are and some built-in higher order functions. We also learned how to create our own higher order function.

In a nutshell, a higher order function is a function that may receive a function as an argument and can even return a function. higher order functions are just like regular functions with an added ability of receiving and returning other functions are arguments and output.

That’s all for now. Thank you for reading and we hope this article will be very helpful to understand the basics of higher order function. Stay tuned for more amazing content. Peace out! 🖖

Resource

How useful was this post?

Click on a star to rate it!

Average rating 0 / 5. Vote count: 0

No votes so far! Be the first to rate this post.

We are sorry that this post was not useful for you!

Let us improve this post!

Tell us how we can improve this post?

Similar articles you may like

Sunil Pradhan

Hi there 👋 I am a front-end developer passionate about cutting-edge, semantic, pixel-perfect design. Writing helps me to understand things better.

Add comment

Stay Updated

Want to be notified when our article is published? Enter your email address below to be the first to know.

Sunil Pradhan

Hi there 👋 I am a front-end developer passionate about cutting-edge, semantic, pixel-perfect design. Writing helps me to understand things better.