Functional Programming
Pure functions, immutability, composition, currying, and higher-order functions — the functional style in JavaScript.
Passing a function straight into map (point-free style) is only safe when that function takes exactly the arguments map gives it. map calls your function with three arguments: the value, its index and the whole array. parseInt takes a second argument, the radix (number base), so it receives the index as the base. Index 0 means "guess", base 1 is invalid, and base 2 can't read "10" as ten.
const input = ["10", "10", "10", "10"]; // e.g. quantities read from a form
console.log(input.map(parseInt)); // point-free: pass parseInt directly
// [ 10, NaN, 2, 3 ]The functional style — build programs from small, reliable pieces
Functional programming is a way of writing code where every function is a pure calculation: same input always gives the same output, and nothing is modified. JavaScript isn't a purely functional language, but applying FP ideas makes code much easier to test and reason about.
Pure functions — predictable and testable
// Impure — depends on external state, unpredictable:
let tax = 0.18;
function calculateTotal(price) {
return price + price * tax; // depends on external 'tax' variable
}
// If tax changes, the function gives different results for the same price!
// Pure — all inputs are parameters, no external dependencies:
function calculateTotalPure(price, taxRate) {
return price + price * taxRate;
}
console.log(calculateTotalPure(100, 0.18)); // always 118
console.log(calculateTotalPure(100, 0.18)); // still 118
// Pure functions are trivial to test:
// test("100 at 18% = 118", () => assert(calculateTotalPure(100, 0.18) === 118))Immutability — never mutate, always return new
// Mutable (bad for FP):
function addItemMutable(cart, item) {
cart.push(item); // modifies the original — side effect!
return cart;
}
// Immutable (FP style):
function addItem(cart, item) {
return [...cart, item]; // returns a NEW array, original unchanged
}
const cart1 = ["apple", "banana"];
const cart2 = addItem(cart1, "cherry");
console.log(cart1); // ["apple", "banana"] — unchanged
console.log(cart2); // ["apple", "banana", "cherry"] — new array
// Same for objects:
const user = { name: "Alice", score: 90 };
const updated = { ...user, score: 95 }; // new object
console.log(user.score); // 90 — original safe
console.log(updated.score); // 95map / filter / reduce — the FP trinity
const products = [
{ name: "Laptop", price: 60000, inStock: true },
{ name: "Phone", price: 25000, inStock: false },
{ name: "Tablet", price: 30000, inStock: true },
{ name: "Monitor", price: 15000, inStock: true },
];
// filter — keep items matching a condition:
const available = products.filter(p => p.inStock);
// map — transform each item:
const discounted = available.map(p => ({
...p,
price: Math.round(p.price * 0.9),
discount: "10% off"
}));
// reduce — fold into a single value:
const total = discounted.reduce((sum, p) => sum + p.price, 0);
console.log(`${discounted.length} items available`);
console.log(`Total: ₹${total.toLocaleString("en-IN")}`);
// Chain them:
const totalDiscount = products
.filter(p => p.inStock)
.map(p => p.price * 0.1)
.reduce((sum, d) => sum + d, 0);
console.log(`Total savings: ₹${totalDiscount.toLocaleString("en-IN")}`);Higher-order functions — functions that work with functions
// A function that returns another function:
function multiplier(factor) {
return (n) => n * factor;
}
const double = multiplier(2);
const triple = multiplier(3);
const halve = multiplier(0.5);
console.log(double(10)); // 20
console.log(triple(10)); // 30
console.log(halve(10)); // 5
// A function that takes another function:
function applyTwice(fn, value) {
return fn(fn(value));
}
console.log(applyTwice(double, 3)); // 12 (doubled twice)double and applyTwice above are safe to pass around because they use only the arguments they're meant to get. Before you pass any other function, built-ins especially, check how many parameters it takes. When in doubt, wrap it in a small arrow function that passes exactly what it needs:
const input = ["10", "10", "10", "10"];
console.log(input.map(s => parseInt(s, 10))); // say exactly which arguments it gets
console.log(input.map(Number)); // Number takes one argument, so it's safe
// [ 10, 10, 10, 10 ]
// [ 10, 10, 10, 10 ]Official Sources
- MDN Web Docs — Array.prototype.map()
- MDN Web Docs — Array.prototype.reduce()
- MDN Web Docs — Closures
- Professor Frisby's Mostly Adequate Guide to FP
The Codex links only to official documentation and the ECMAScript specification. No third-party tutorials.