Dev.to WebDev 🛠 Dev 👁 0 📖 4 min read

Functional Programming in JavaScript: Principles, Patterns, and Practical Examples

Functional programming is a programming style that treats computation as the evaluation of functions and encourages predictable transformations of data. JavaScript supports this style naturally through first-class functi

Functional programming is a programming style that treats computation as the evaluation of functions and encourages predictable transformations of data. JavaScript supports this style naturally through first-class functions, closures, higher-order functions, immutability patterns, and array methods.

You do not need to write JavaScript in a purely functional way to benefit from these ideas. Understanding functional programming helps you build code that is easier to test, compose, reuse, and reason about, especially as applications grow in complexity.

Core Functional Programming Concepts in JavaScript

One of the most important functional programming principles is keeping functions pure whenever practical. A pure function produces the same output for the same input and does not modify external state, which makes its behavior easier to understand and test. For example, a function that calculates a discounted price from a product and discount percentage is predictable because it does not depend on hidden variables.

Higher-order functions are another foundation of functional JavaScript. A higher-order function either accepts another function as an argument or returns a function, which enables reusable abstractions such as map, filter, reduce, and custom transformation pipelines. JavaScript arrays make these patterns especially convenient because methods such as map and filter return new arrays instead of requiring manual mutation.

Immutability is closely related to this approach. Instead of changing an existing object or array, functional code often creates a new value containing the desired changes. This can reduce unexpected side effects and makes state transitions easier to track in applications, although immutability should be applied thoughtfully when performance or memory usage is important.

The following example combines pure functions, composition, higher-order functions, immutable transformations, filtering, mapping, and reducing. The console logs intentionally show each stage so you can follow how raw product data moves through a functional processing pipeline.

const products = [
  { id: 1, name: "Laptop", category: "electronics", price: 75000, stock: 8 },
  { id: 2, name: "Headphones", category: "electronics", price: 4500, stock: 15 },
  { id: 3, name: "Keyboard", category: "electronics", price: 3200, stock: 0 },
  { id: 4, name: "Backpack", category: "accessories", price: 2800, stock: 12 },
  { id: 5, name: "Mouse", category: "electronics", price: 1800, stock: 20 }
];

console.log("Step 1: Original product data");
console.log(products);

// Pure function: calculates a discounted price without changing the product.
const applyDiscount = (product, percentage) => ({
  ...product,
  price: product.price - (product.price * percentage) / 100
});

console.log("\nStep 2: Applying a 10% discount without mutating products");
const discountedProducts = products.map(product => applyDiscount(product, 10));
console.log(discountedProducts);

// Pure predicate used by filter to keep only available products.
const isInStock = product => product.stock > 0;

console.log("\nStep 3: Filtering products that are currently in stock");
const availableProducts = discountedProducts.filter(isInStock);
console.log(availableProducts);

// Higher-order function: returns a function that selects a category.
const byCategory = category => product => product.category === category;

console.log("\nStep 4: Selecting electronics with a reusable predicate");
const electronics = availableProducts.filter(byCategory("electronics"));
console.log(electronics);

// Pure transformation that creates a smaller view model for reporting.
const toSummary = product => ({
  name: product.name,
  price: Math.round(product.price),
  stock: product.stock,
  inventoryValue: Math.round(product.price * product.stock)
});

console.log("\nStep 5: Mapping products into report summaries");
const summaries = electronics.map(toSummary);
console.log(summaries);

// Reduce combines many values into one final result.
const calculateInventoryValue = (total, product) =>
  total + product.inventoryValue;

console.log("\nStep 6: Calculating total inventory value");
const totalInventoryValue = summaries.reduce(calculateInventoryValue, 0);
console.log(`Total inventory value: ₹${totalInventoryValue}`);

// Function composition applies several functions from right to left.
const compose = (...functions) => value =>
  functions.reduceRight((result, fn) => fn(result), value);

const getElectronics = products => products.filter(byCategory("electronics"));
const getAvailable = products => products.filter(isInStock);
const summarize = products => products.map(toSummary);

const buildElectronicsReport = compose(
  summarize,
  getElectronics,
  getAvailable,
  products => products.map(product => applyDiscount(product, 10))
);

console.log("\nStep 7: Running the complete composed pipeline");
const report = buildElectronicsReport(products);
console.log(report);

// The original data remains unchanged after every transformation.
console.log("\nStep 8: Verifying immutability of the original data");
console.log("Original Laptop price:", products[0].price);
console.log("Processed Laptop price:", report[0].price);

// Reduce can also create useful aggregate statistics.
const totalUnits = summaries.reduce((total, product) => total + product.stock, 0);
const averagePrice = summaries.length
  ? summaries.reduce((total, product) => total + product.price, 0) / summaries.length
  : 0;

console.log("\nStep 9: Calculating aggregate statistics");
console.log("Total units:", totalUnits);
console.log("Average product price:", Math.round(averagePrice));

console.log("\nFunctional pipeline completed successfully.");

Conclusion

Functional programming gives JavaScript developers a practical way to make data transformations explicit and predictable. Pure functions isolate logic, higher-order functions improve reuse, and methods such as map, filter, and reduce provide expressive tools for transforming collections.

The goal is not to eliminate every loop, mutation, or state change from an application. Instead, use functional techniques where they make the code easier to understand, test, compose, and maintain, while still choosing imperative approaches when they are clearer or more efficient.

📰 Read the original article on Dev.to WebDev

Originally published by Dev.to WebDev. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.