More Functional Programming Concepts
JavaScript supports many functional programming techniques, but it is not a purely functional language. This section covers a few more functional programming concepts. JavaScript does not enforce them, but you should still know what they are.
Pure Functions
A pure function always returns the same output for the same input and has no side effects. It does not modify any external state.
// Pure function
function add(a, b) {
return a + b;
}
// Impure function
let count = 0;
function increment() {
count++; // Side effect: modifying external state
return count;
}
Pure functions are easier to test, debug, and reason about. Write pure functions where you can, and keep the side effects in specific parts of your code.
Immutability
Immutability means not changing data after it is created. Instead of modifying existing objects or arrays, you create new ones with the desired changes.
const person = { name: "John", age: 30 };
// Mutating (avoid this in FP style)
person.name = "Jane";
// Immutable approach (create a new object)
const updatedPerson = { ...person, name: "Jane" };
JavaScript does not enforce immutability. The const keyword only prevents reassignment, not mutation:
const arr = [1, 2, 3];
arr.push(4); // This works! The array is mutated.
You can use Object.freeze() for shallow immutability, or a library like Immutable.js if you need more than that.
Avoiding Side Effects
Side effects include:
- Modifying external variables
- Writing to the console or files
- Making network requests
- Modifying the DOM
Real applications need side effects. Functional programming does not tell you to avoid them entirely; it tells you to keep them out of your core logic:
// Core logic: pure function
function calculateTotal(items) {
return items.reduce((sum, item) => sum + item.price, 0);
}
// Side effect isolated to a specific place
function displayTotal(items) {
const total = calculateTotal(items);
console.log(`Total: $${total}`); // Side effect here
}
What JavaScript Lacks
Purely functional languages like Haskell, Erlang, and Clojure have features that JavaScript does not support:
-
Tail Call Optimization: Allows recursive functions to run without growing the call stack. JavaScript technically specified this in ES6, but most engines do not implement it.
-
Pattern Matching: Destructures and matches complex data structures directly. JavaScript’s destructuring is limited by comparison.
-
Lazy Evaluation: Delays computation until the result is needed. JavaScript evaluates expressions eagerly by default.
-
Algebraic Data Types: Types like
MaybeandEitherfor handling nullability and errors. Libraries like Ramda.js can provide similar patterns.
Conclusion
JavaScript is a multi-paradigm language. You can write object-oriented, procedural, or functional code. Understanding functional programming concepts helps you write more predictable, testable code, even if JavaScript does not enforce these principles.