Search Knowledge

© 2026 LIBREUNI PROJECT

Modern JavaScript & Ecosystem / Functional Foundations

Pure Functions and Immutability

The Concept of Purity

In functional programming, a Pure Function is a function that possesses two defining characteristics:

  1. Determinism: Given the same input, it will always return the same output.
  2. No Side Effects: It does not modify any state outside its scope or interact with the “outside world” (e.g., logging to console, network calls, modifying global variables).

Determinism and Idempotency

A deterministic function depends solely on its input arguments. If a function uses Math.random(), Date.now(), or reads from a global configuration that can change, it is impure.

Pure functions are idempotent in the context of their return values: calling them multiple times with the same input yields identical results. This makes them significantly easier to test and allows for memoization (caching the results of expensive computations).

Side Effects: The Enemies of Predictability

A side effect is any observable change outside the function’s return value.

  • Modifying an argument (if it’s a reference type).
  • Mutating a global variable.
  • Writing to a database or file system.
  • Triggering a UI update.

While applications must have side effects to be useful (otherwise they wouldn’t perform I/O), the goal of functional programming is to isolate side effects to thin wrappers at the edges of the system, keeping the core logic pure.

Immutability: Protecting State

Immutability means that once a piece of data is created, it cannot be changed. In JavaScript, primitive types (strings, numbers, booleans) are naturally immutable. However, objects and arrays are mutable by default.

The Problem with Mutation

Mutation makes it difficult to track how state changes over time. If multiple parts of a program share a reference to the same object and one part mutates it, the other parts may crash or behave unpredictably.

javascript
1const original = { x: 1, y: 2 };
2const shallowCopy = original;
3 
4shallowCopy.x = 99; // Mutation affects both variables
5 
6console.log(original.x); // 99

Achieving Immutability in JavaScript

To maintain immutability, we create new versions of data structures instead of modifying them in place. This is facilitated by the Spread Operator (...).

javascript
1const state = { user: "Alice", roles: ["admin"] };
2 
3// Create a new state object with an updated field
4const nextState = {
5 ...state,
6 user: "Bob",
7 roles: [...state.roles, "editor"] // Deep copy the array too
8};
9 
10console.log(state.user); // "Alice" (Unchanged)
11console.log(nextState.user); // "Bob"

Structural Sharing

One might worry that creating new objects for every change is inefficient. However, modern JavaScript engines and functional libraries use “Structural Sharing”. If you create a new object from an old one, the new object’s properties can point to the same memory locations as the old object’s properties for any data that hasn’t changed. Only the modified parts are allocated fresh memory.

Benefits of Immutability

  1. Time-Travel Debugging: Because state is never overwritten, you can keep a history of past states and move back and forth between them.
  2. Referential Equality: Checking if an object has changed becomes a simple identity check (obj1 === obj2) rather than a deep recursive comparison. This is critical for performance optimizations in frameworks like React.
  3. Thread Safety: While JavaScript is single-threaded, immutable data structures prevent complex bugs in asynchronous code where multiple closures might otherwise struggle over a shared mutable object.