Higher-order Functions And Functional Programming In JavascriptA beginner's guide to higher-order functions in javascript
  Tagged under:  # Javascript
  Date:          
  Time to read:  7 min read
  Author:  Vigneshwar Balaji

Higher-order Functions And Functional Programming In Javascript

Introduction:

Higher-order functions were initially introduced in Javascript in the year 2011 as an ES5 feature. In 2011 after the introduction of ES5 features, Javascript saw a huge shift from being a toy programming language that is used to make cute animations on websites to a major scalable language. This is the era in which the hidden features of Javascript sent shockwaves across the programming community and set a trend for rebirth of the functional programming.

The rebirth of functional programming:

After the initial whisperings on Javascript on its ability to do complex tasks the introduction of ES6 features in 2015 set an initial trend for the rebirth of functional programming. This is the time at which features like arrow functions, currying and lambda expressions were introduced. Arrow functions acted as fuel for the explosion of Javascript as a major functional programming language. Nowadays, it is difficult to see an enterprise-grade application without functional programming in Javascript.

What is functional programming?

Functional programming is a form of programming where you can pass functions as an argument to other functions and it can return function as an output. There are lots of functional programming languages Javascript is one of them and it took the lead in the trend when ES6 features were introduced.

Functions as first-class citizens:

In almost all functional programming languages functions are usually treated as objects, which means you can do all the stuff that you do with other types such as number, object and string in functions as well.

Javascript and first-class functions:

In Javascript, functions are treated as special types of objects called function objects. For example:

const simpleFunction = () => {
    console.log("Hello World!")
}

simpleFunction() //Output: Hello World!

To prove functions as objects we can also add random properties to them like we usually do with objects. For example:

simpleFunction.number = 'one'

console.log(simpleFunction.number) //Output: one

Note: Assigning random properties to function objects in Javascript is not a recommended approach this is done as a proof of concept.

In Javascript, everything you do with other types like objects, number and string you can also do with functions as well. You can pass them as arguments to other functions, assign them to a variable and return functions etc.,

Assign functions to variables:

Example: The below function prints sum of a given number and it is an example of first-class function.

const sum = function(randomNumber){
    return randomNumber + randomNumber
}

console.log(sum(5)) //Output: 10

Pass function as arguments to another function:

Example:

function exampleFunction(someString, callback) {
  console.log(someString)
  callback()
}

function exampleCallbackFunc() {
  console.log('This is a callback function')
}

exampleFunction('This will be executed before invoking callback.', exampleCallbackFunc) 
/*
Output:
This will be executed before invoking callback.
I am a callback function
 */

Passing a function as an argument to another function is a common practice in the Javascript world. 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 have been completed. This works like this because JavaScript is a single-threaded language. This means that only one operation can be executed at a time. So, when you pass a callback function, and invoke it at the end, it will be invoked when all preceding operations are completed.

Return functions:

Returning functions from other functions is another thing that you can do with Javascript, as functions are a type of object in Javascript. Here in the below javascript example everyone is replaced with friends in the passed string.

Example:

function replaceTheGivenString(word, replacement, text) {
  return function(text) {
    return text.replace(word, replacement)
  }
}
const replacedString = replaceTheGivenString(/everyone/g, 'friends')
console.log(replacedString('Hey everyone'))

//Output: Hey friends

The last two functions of this heading are examples of custom higher-order functions.

Difference between first-class functions and higher-order functions:

The only difference between a higher-order function and a first-class function is that first-class functions don't take functions as the parameter and also they don't return a function. As we now know the difference between the two let us move towards higher-order functions.

Higher-order functions:

As we already discussed higher-order functions are functions that take functions as argument and/or return functions. One interesting thing about JavaScript is that there are already some built-in higher-order functions. Some of the in-built higher-order functions are map(), reduce(), filter() etc.,

Native higher-order functions:

Let's have a look at some of the built-in higher-order functions and let us compare them with normal Javascript functions.

Map():

The first one of built-in higher-order functions is map(). The map function will create a new array by calling the callback function which is passed as an argument in every element of the given input array. Let's say we have an array of numbers and we want to create a new array with squared numbers of an input array. The below example explains the same with and without a map() function. This map() function accepts 3 arguments element, index and array.

Example: Without native map() function:

let firstArray = [1,2,3,4,5]
let secondArray = []
for(let index = 0; index < firstArray.length; index++){
    secondArray.push(firstArray[index] * firstArray[index])
}
console.log(secondArray) //Output:[1,4,9,16,25]

With in-built map() function:

let firstArray = [1,2,3,4,5]
let secondArray = firstArray.map(element => element*element)
console.log(secondArray) //Output:[1,4,9,16,25]

Filter():

The second in-built function that we are going to see is filter(). The filter() function will help you to iterate over an array and create a new array only with elements that meet specific conditions. The below example loops through all the months of the given input array and creates a new array with those months with an even number string length. This filter() function accepts 3 arguments element, index and array.

Example: Without native filter() function:

const month = ["January","February","March","April","May","June","July","August","September","October","November","December"];
const filteredMonths = []

for (let index = 0; index < month.length; index++) {
  if (month[index].length % 2 === 0) {
    filteredMonths.push(month[index])
  }
}
console.log(filteredMonths)
//Output: [ 'February', 'June', 'July', 'August', 'November', 'December' ]

With native filter() function:

const months = ["January","February","March","April","May","June","July","August","September","October","November","December"];
console.log(months.filter(month => month.length % 2 === 0))
//Output: [ 'February', 'June', 'July', 'August', 'November', 'December' ]

Reduce();

The reduce() function runs the callback function on each element of the array and produces a single output. The reduce method accepts two parameters:

  1. The reducer callback function.
  2. An optional initial value.

The reducer callback function accepts 4 arguments:

  1. Accumulator
  2. CurrentValue
  3. CurrentIndex and
  4. SourceArray

If an initial value is provided accumulator value will be equal to initialValue and currentValue will be equal to the first element of the array. If the initialValue is not provided means then the accumulator value will be equal to the first element of the array and currentValue will be equal to the second element of the array.

Let us assume we have to find the average weight of a group of 5 people. The following program solves such a problem with or without reduce() function and explains the same.

Example: Without native reduce() function:

const persons = [
  {
    name: 'Rob',
    weight: 75
  },
  {
    name: 'Cyan',
    weight: 98 
  },
  {
    name: 'John',
    weight: 55
  },
  {
    name: 'Venessa',
    weight: 53
  },
  {
    name: 'Richards',
    weight: 63
  }
]

let totalWeight = 0;

for(person of persons){
    totalWeight += person.weight
}
const averageWeight = totalWeight/5;
console.log(averageWeight)
//Output: 68.8

With native reduce() function:

const persons = [
  {
    name: 'Rob',
    weight: 75
  },
  {
    name: 'Cyan',
    weight: 98 
  },
  {
    name: 'John',
    weight: 55
  },
  {
    name: 'Venessa',
    weight: 53
  },
  {
    name: 'Richards',
    weight: 63
  }
]
function averageWeight(totalWeight, persons) {
  return totalWeight + persons.weight
}
console.log(persons.reduce(averageWeight, 0)/5)
//Output: 68.8

From the above examples, it is clear that functional programming or higher-order functions reduced the lines of code as well as made the code cleaner and less verbose.

Conclusion:

This article explored the history of higher-order functions and functional programming in Javascript. We also learned about first-class functions and higher-order functions, the differences between higher-order functions and first-class functions and how to use them in practical applications through some code samples.

In short higher-order functions are functions with the ability to receive and return a function as a value. It makes the code cleaner and easily understandable for everyone.

Thanks for reading.