Welcome to the world of higher-order functions (HOFs) in JavaScript! This chapter dives deep into these powerful functions that operate on other functions, allowing you to write more concise, expressive, and reusable code.
In JavaScript, a higher-order function is a function that meets one or both of these criteria:
These capabilities allow HOFs to control the behavior of other functions, promoting abstraction and modularity in your code.
Here are some of the most commonly used higher-order functions in JavaScript:
map()
:
const numbers = [1, 2, 3];
const doubledNumbers = numbers.map(number => number * 2); // [2, 4, 6]
const names = ['Alice', 'Bob', 'Charlie'];
const greetings = names.map(name => `Hello, ${name}!`); // ["Hello, Alice!", "Hello, Bob!", "Hello, Charlie!"]
numbers
and names
to hold sample data.map()
function is used on both arrays.map()
takes a callback function as an argument. This callback function is executed for each element in the original array.number => number * 2
doubles each element in the numbers
array.name =>
Hello, ${name}!
constructs a greeting string for each name in the names
array using template literals.map()
function creates a new array (doubledNumbers
and greetings
) containing the results of the callback function for each element in the original array.filter()
:This function iterates over an array, applies a provided callback function to each element, and returns a new array containing only the elements for which the callback function returns true
.
const numbers = [1, 2, 3, 4, 5];
const evenNumbers = numbers.filter(number => number % 2 === 0); // [2, 4]
const products = [
{ name: 'Shirt', price: 20 },
{ name: 'Hat', price: 15 },
{ name: 'Jacket', price: 50 }
];
const expensiveProducts = products.filter(product => product.price > 25); // [{ name: 'Jacket', price: 50 }]
numbers
with numbers and an array products
with objects representing products.filter()
function is used on both arrays.filter()
takes a callback function as an argument. This callback function is executed for each element in the original array.number => number % 2 === 0
checks if the number is even. The filter()
function creates a new array (evenNumbers
) containing only the even numbers from the original numbers
array.product => product.price > 25
checks if the product’s price is greater than 25. The filter()
function creates a new array (expensiveProducts
) containing only the expensive products from the original products
array.
function greet(greeting, name) {
return `${greeting}, ${name}!`;
}
const morningGreet = partialApplication(greet, 'Good Morning');
console.log(morningGreet('Alice')); // Outputs: "Good Morning, Alice!"
reduce()
:This function applies a function against an accumulator and each element in an array (from left to right) to reduce it to a single value.
const numbers = [1, 2, 3, 4];
const sum = numbers.reduce((accumulator, number) => accumulator + number, 0); // 10 (initial accumulator is 0)
const words = ['hello', 'world'];
const sentence = words.reduce((accumulator, word) => accumulator + ' ' + word, ''); // "hello world" (initial accumulator is an empty string)
numbers
with numbers and an array words
with strings.reduce()
function is used on both arrays.reduce()
takes two arguments: a callback function and an initial accumulator value.accumulator
: The value that holds the result of the previous callback function call or the initial accumulator value.number
(for the numbers
array) or word
(for the words
array): The current element being processed in the array.(accumulator, number) => accumulator + number
adds the current number (number
) to the accumulator (accumulator
). The initial accumulator value is set to 0
. The reduce()
function iterates through the numbers
array, adding each number to the accumulator, resulting in the sum of all elements.(accumulator, word) => accumulator + ' ' + word
concatenates a space and the current word (word
) to the accumulator (accumulator
). The initial accumulator value is set to an empty string (''
). The reduce()
function iterates through the words
array, building a sentence string by combining all words with spaces.HOFs can be used for partial application, where a function is pre-configured with some arguments. This is useful for creating functions with a specific behavior based on the provided arguments.
function greet(greeting, name) {
return `${greeting}, ${name}!`;
}
const morningGreet = partialApplication(greet, 'Good Morning');
console.log(morningGreet('Alice')); // Outputs: "Good Morning, Alice!"
greet
that takes two arguments: greeting
and name
. It constructs and returns a greeting string.partialApplication
that isn’t explicitly defined here. This function likely creates a new function by pre-configuring an argument for the original function.partialApplication(greet, 'Good Morning')
creates a new function (assigned to morningGreet
) that already has the first argument (greeting
) set to "Good Morning"
.morningGreet('Alice')
, it essentially calls the original greet
function with the pre-configured "Good Morning"
and the provided argument "Alice"
for the name
parameter, resulting in the final greeting output.forEach()
:This function iterates over an array and calls a provided function once for each element. It doesn’t return a new array, but it’s useful for performing side effects on each element (like logging or DOM manipulation).
const numbers = [1, 2, 3];
numbers.forEach(number => console.log(number * 2)); // Outputs: 2, 4, 6 (no new array returned)
numbers
with numbers.forEach()
function is used on the numbers
array.forEach()
takes a callback function as an argument. This callback function is executed for each element in the original array.number => console.log(number * 2)
doubles each element and logs the result to the console. However, forEach()
doesn’t return a new array. It’s primarily used for performing side effects on each element, like logging or DOM manipulation in web development.Higher-order functions are a cornerstone of functional programming in JavaScript. By understanding their capabilities and how to use them effectively, you can elevate your code to be more concise, expressive, reusable, and composable.Here are some additional points to consider:Choosing the Right HOF: Selecting the appropriate HOF for your task depends on the desired transformation or behavior you want to achieve with your data. Readability and Maintainability: While HOFs can be powerful, strive for clear and readable code. Ensure the use of HOFs enhances, not hinders, the understandability of your program. Functional Programming Paradigm: HOFs are central to the functional programming paradigm. Learning more about functional programming concepts can further enrich your understanding of HOFs and their applications. By mastering higher-order functions, you'll unlock a new level of power and flexibility in your JavaScript development journey.