The Functional Paradigm
JavaScript is a multi-paradigm language that treats functions as first-class citizens. This means functions can be assigned to variables, passed as arguments to other functions, and returned from functions. This capability is the foundation of Functional Programming (FP).
Higher-Order Functions (HOFs)
A Higher-Order Function is a function that either:
- Takes one or more functions as arguments.
- Returns a function as its result.
HOFs allow for the abstraction of actions, not just values. This leads to code that is more modular, reusable, and declarative (focusing on “what” to do rather than “how” to do it).
Declarative Array Transformations
The most common application of HOFs in JavaScript is manipulating collections. Instead of using imperative for loops, we use declarative methods on the Array.prototype.
Map: Transforming Elements
The map() method creates a new array by applying a callback function to every element of the calling array. It does not mutate the original array.
const temperaturesCelsius = [0, 10, 25, 30];
const temperaturesFahrenheit = temperaturesCelsius.map(c => (c * 9/5) + 32);
Filter: Selecting Elements
The filter() method creates a new array containing only the elements that satisfy a provided predicate function.
const users = [{ name: 'Alice', active: true }, { name: 'Bob', active: false }];
const activeUsers = users.filter(user => user.active);
Reduce: Consolidating Data
The reduce() method is the most powerful HOF. It executes a “reducer” function on each element, resulting in a single output value (an object, a number, or even another array).
Currying and Partial Application
Currying is the technique of converting a function that takes multiple arguments into a sequence of functions that each take a single argument. This allows for Partial Application, where you fix some arguments of a function and produce a new function for the remaining ones.
Why Functional Programming?
- Predictability: Functional code often relies on pure functions, making it easier to reason about and test.
- Concurrency: Because FP emphasizes immutability, it avoids race conditions associated with shared mutable state.
- Brevity: Declarative code is typically more concise than imperative equivalents.