Javascript Functional Programming: Immutability, Pure Functions, and Composition

Functional Programming in JavaScript: Immutability, Pure Functions, and Composition
7 views

Functional programming is not a religion — it is a set of constraints that make code easier to reason about, test, and compose. Pure functions with no side effects are trivially testable. Immutable data prevents spooky action at a distance. Function composition builds complex behavior from simple pieces. You can apply FP principles in any JavaScript codebase today without going full Haskell. This guide focuses on JavaScript functional programming, with the production trade-offs senior engineers need to make sound decisions.

⚡ TL;DR: Pure function: same inputs → same output, no side effects. Immutability: never mutate, always create new. Higher-order functions: functions that take or return functions. Composition: f(g(x)) builds pipelines. Currying: convert f(a,b) into f(a)(b) for partial application.

Pure functions — the foundation — JavaScript functional programming

// Impure: depends on or modifies external state
let total = 0;
function addToTotal(amount) {
  total += amount; // Mutates external state — impure!
  return total;    // Output depends on external state — impure!
}
// addToTotal(5) might return 5, 15, 100 — depends on external state

// Pure: same inputs → same output, zero side effects
function add(a, b) { return a + b; }
add(2, 3); // Always 5. Testable in one line. Composable.

// Benefits of pure functions:
// - Trivially testable: no mocks, no setup, just input → output
// - Cacheable (memoizable): same input → same output → cache it
// - Parallelizable: no shared state = no race conditions
// - Debuggable: output is fully determined by input

Immutability — never mutate

// Mutation causes bugs that are hard to trace
const user = { name: 'Alice', scores: [10, 20, 30] };
function addScore(user, score) {
  user.scores.push(score); // MUTATION: modifies original!
  return user;
}
// Caller's user is now modified — unexpected!

// Immutable: always create new
function addScore(user, score) {
  return { ...user, scores: [...user.scores, score] };
}
// Original user unchanged. New object returned.

// Array operations:
const arr = [1, 2, 3];
// Mutable (avoid): push, pop, shift, unshift, splice, sort, reverse
// Immutable (use):
arr.map(x => x * 2)          // New array
arr.filter(x => x > 1)       // New array
[...arr, 4]                   // New array with 4 added
arr.slice(0, -1)              // New array without last
[...arr].sort()               // Sort copy, not original

Higher-order functions and composition

// Higher-order: functions that take/return functions
const double = x => x * 2;
const addOne = x => x + 1;
const isEven = x => x % 2 === 0;

// Compose: apply functions right to left
const compose = (...fns) => x => fns.reduceRight((acc, fn) => fn(acc), x);
const pipe = (...fns) => x => fns.reduce((acc, fn) => fn(acc), x);

const transform = pipe(
  x => x * 2,    // 5 → 10
  x => x + 1,    // 10 → 11
  x => x ** 2    // 11 → 121
);
transform(5); // 121

// Real pipeline:
const processUsers = pipe(
  users => users.filter(u => u.active),
  users => users.map(u => ({ ...u, name: u.name.trim() })),
  users => users.sort((a, b) => a.name.localeCompare(b.name)),
  users => users.slice(0, 10)
);
processUsers(rawUsers); // Pure pipeline, each step testable in isolation

Currying and partial application

// Currying: f(a, b, c) → f(a)(b)(c)
const curry = fn => {
  return function curried(...args) {
    if (args.length >= fn.length) return fn(...args);
    return (...more) => curried(...args, ...more);
  };
};

const add = curry((a, b, c) => a + b + c);
add(1)(2)(3);   // 6
add(1, 2)(3);   // 6
add(1)(2, 3);   // 6

// Partial application: pre-fill some arguments
const multiply = (factor) => (x) => x * factor;
const double = multiply(2);
const triple = multiply(3);
[1, 2, 3].map(double); // [2, 4, 6]
[1, 2, 3].map(triple); // [3, 6, 9]

// Real use: customized utility functions
const filterBy = curry((field, value, arr) => arr.filter(item => item[field] === value));
const activeUsers = filterBy('status', 'active');
const premiumUsers = filterBy('tier', 'premium');
activeUsers(users);  // Filter for active
premiumUsers(users); // Filter for premium

FP cheat sheet

  • ✅ Pure functions: same input → same output, no side effects
  • ✅ Immutability: spread operator, Object.freeze, Immer for complex state
  • ✅ map/filter/reduce over imperative loops for data transformation
  • ✅ Function composition with pipe() for readable data pipelines
  • ✅ Currying for reusable utility functions with partial application
  • ❌ FP is not “no classes ever” — use the right tool for each problem
  • ❌ Don’t over-abstract simple code — readability > purity

Functional programming concepts power the generator function patterns — generators are lazy functional pipelines. The React performance guide builds on FP: pure components, immutable state updates. External reference: Mostly Adequate Guide to Functional Programming.

Recommended Reading

→ Designing Data-Intensive Applications — Essential for every senior developer. Distributed systems, databases, and production architecture.

→ The Pragmatic Programmer — Timeless engineering wisdom for writing better code at any level.

Affiliate links. We earn a small commission at no extra cost to you.

Free Weekly Newsletter

🚀 Don’t Miss the Next Cheat Code

Join 1,000+ senior developers getting expert-level JS, Python, AWS, system design and AI secrets every week. Zero fluff, pure signal.

✓ No spam✓ Unsubscribe anytime✓ Expert-level only

Related: TypeScript for JavaScript Developers: The Migration Guide That Actually Works · JavaScript Closures, Scope, and the Module Pattern: The Complete Guide · JavaScript Promises: The Complete Guide From Basics to Advanced Patterns

Related: JavaScript Async/Await: Every Pattern, Pitfall, and Performance Trick · JavaScript Array Methods: Every Method Explained With Real Examples · Design Patterns in JavaScript: The Gang of Four Patterns Every Senior Dev Uses

Watch: 100 Days of AI on YouTube

Short videos from CheatCoders, one AI engineering topic per day.

Open the playlist on YouTube · CheatCoders on YouTube

Deep-dive PDF

Get the expanded guide for this post — extra diagrams-style checklists, failure modes, and a production walkthrough. Free when you subscribe to CheatCoders.

Already subscribed? or open the subscribe page.


Discover more from CheatCoders

Subscribe to get the latest posts sent to your email.

Comments

No comments yet. Why don’t you start the discussion?

Leave a comment

No account needed. Name and email are optional.