Online Compiler logoOnline Compiler

JavaScript Tutorial

JavaScript Pure Functions

Master pure functions for predictable, testable, and maintainable code.

Why this matters

Pure functions are a cornerstone of functional programming and modern JavaScript. They make code easier to test, reason about, and maintain.

What are Pure Functions?

A pure function is a function where the return value is determined only by its input values, without observable side effects.

Pure functions have two key characteristics: 1) Given the same input, they always return the same output, and 2) They don't modify anything outside their scope.

Pure vs Impure Functions

// PURE FUNCTION - same input always gives same output
function add(a, b) {
  return a + b;
}

console.log(add(2, 3)); // 5
console.log(add(2, 3)); // 5 - always the same

// IMPURE FUNCTION - depends on external state
let globalMultiplier = 2;

function multiplyImpure(x) {
  return x * globalMultiplier; // depends on global variable
}

console.log(multiplyImpure(5)); // 10
globalMultiplier = 3;
console.log(multiplyImpure(5)); // 15 - different output!

// PURE FUNCTION - takes multiplier as parameter
function multiplyPure(x, multiplier) {
  return x * multiplier;
}

console.log(multiplyPure(5, 2)); // 10
console.log(multiplyPure(5, 2)); // 10 - always the same

Pure functions are predictable and testable because their output depends only on their inputs.

Pure Functions vs Impure Functions

Impure functions have side effects like modifying global variables, making API calls, logging, or mutating input parameters.

Side Effects Examples

// IMPURE - modifies global state
let count = 0;
function incrementImpure() {
  count++;
  return count;
}

// PURE - returns new value without modifying state
function incrementPure(num) {
  return num + 1;
}

// IMPURE - mutates input object
function addPropertyImpure(obj) {
  obj.newProp = "added";
  return obj;
}

const person = { name: "Alice" };
addPropertyImpure(person);
console.log(person); // { name: "Alice", newProp: "added" } - mutated!

// PURE - creates new object
function addPropertyPure(obj) {
  return { ...obj, newProp: "added" };
}

const person2 = { name: "Bob" };
const person3 = addPropertyPure(person2);
console.log(person2); // { name: "Bob" } - unchanged
console.log(person3); // { name: "Bob", newProp: "added" } - new object

// IMPURE - side effects (logging, API calls)
function fetchUserImpure(id) {
  console.log("Fetching user..."); // side effect
  // make API call
  return { id, name: "User" };
}

// PURE - no side effects
function formatUser(user) {
  return {
    ...user,
    displayName: user.name.toUpperCase()
  };
}

Pure functions avoid modifying external state and side effects.

Benefits of Pure Functions

Pure functions are easier to test, debug, reuse, and parallelize. They're fundamental to functional programming.

Testing Pure Functions

// PURE function - easy to test
function calculateDiscount(price, discountPercent) {
  return price * (1 - discountPercent / 100);
}

// Simple to test - no setup needed
console.log(calculateDiscount(100, 10)); // 90
console.log(calculateDiscount(100, 10)); // 90 (always same)
console.log(calculateDiscount(50, 20)); // 40

// IMPURE function - harder to test
let taxRate = 0.1;

function calculateTotalImpure(price) {
  return price * (1 + taxRate); // depends on global state
}

// Hard to test - need to manage global state
console.log(calculateTotalImpure(100)); // 110
taxRate = 0.2;
console.log(calculateTotalImpure(100)); // 120

// PURE version - easy to test
function calculateTotalPure(price, taxRate) {
  return price * (1 + taxRate);
}

console.log(calculateTotalPure(100, 0.1)); // 110
console.log(calculateTotalPure(100, 0.2)); // 120

Pure functions are testable without mocking or setup.

Working with Arrays and Objects Immutably

When working with data structures, pure functions create new arrays/objects rather than modifying existing ones.

Immutable Operations

// IMPURE - mutates original array
function addItemImpure(arr, item) {
  arr.push(item);
  return arr;
}

const list = [1, 2, 3];
const result = addItemImpure(list, 4);
console.log(list); // [1, 2, 3, 4] - mutated!

// PURE - creates new array
function addItemPure(arr, item) {
  return [...arr, item];
}

const list2 = [1, 2, 3];
const result2 = addItemPure(list2, 4);
console.log(list2); // [1, 2, 3] - unchanged
console.log(result2); // [1, 2, 3, 4]

// IMPURE - sorts original array
const numbers = [3, 1, 2];
numbers.sort();
console.log(numbers); // [1, 2, 3] - mutated!

// PURE - creates sorted copy
function sortPure(arr) {
  return [...arr].sort();
}

const numbers2 = [3, 1, 2];
const sorted = sortPure(numbers2);
console.log(numbers2); // [3, 1, 2] - unchanged
console.log(sorted); // [1, 2, 3]

// IMPURE - modifies object properties
const user = { name: "Alice", age: 25 };
user.age = 26;

// PURE - creates new object with updates
function updateAge(user, newAge) {
  return { ...user, age: newAge };
}

const user2 = { name: "Bob", age: 25 };
const user3 = updateAge(user2, 26);
console.log(user2); // { name: "Bob", age: 25 } - unchanged
console.log(user3); // { name: "Bob", age: 26 }

Pure functions preserve original data by creating new structures instead of mutating.

When to Use Pure Functions

Pure functions should be your default choice. Use impure functions only when necessary, like for I/O operations.

  • Calculations and transformations should be pure
  • Data validation should be pure
  • Formatting and parsing should be pure
  • API calls and logging are necessarily impure
  • Side effects should be isolated from pure logic

Common Mistakes

  • Modifying input objects/arrays: Always create new objects/arrays using spread operator or methods like map/filter instead of mutating inputs.
  • Depending on external variables: Pass all needed data as parameters. Avoid relying on global variables or closure variables that change.
  • Not realizing some methods are impure: Be aware that sort(), reverse(), push(), pop() mutate arrays. Use map(), filter(), slice() for pure operations.

FAQ

What is a pure function?

A pure function always returns the same output for the same input and has no side effects or external dependencies.

Why are pure functions important?

Pure functions are predictable, testable, and easier to debug. They're fundamental to functional programming.

What are side effects?

Side effects are any observable changes outside the function, like modifying global variables, making API calls, logging, or mutating parameters.

Can I use API calls in pure functions?

No, API calls are side effects. Separate pure logic from side effects by handling I/O in a separate layer.