Level up your function writing with reusability, composition, and clean design principles — the habits that separate maintainable code from tangled spaghetti.
Advanced functions in JavaScript refer to techniques and practices that enhance function usability — including reusability, function composition, and clean coding principles — allowing developers to write modular, maintainable, and scalable code.
The core idea is simple: write each function to do one thing well, then combine those small focused functions to build complex behaviour. This approach makes code easier to read, test, and reuse across projects.
Advanced functions are about three habits that make every codebase better:
function calculateArea(length, width) {
return length * width;
}
// Use the same function with different data — no code duplication
console.log(calculateArea(5, 4)); // 20
console.log(calculateArea(10, 2)); // 20
console.log(calculateArea(7, 3)); // 21
function double(x) {
return x * 2;
}
function square(x) {
return x * x;
}
// Compose: output of double() becomes input of square()
let result = square(double(3));
// double(3) → 6
// square(6) → 36
console.log(result); // 36
// You can also store intermediate results
let doubled = double(4); // 8
let squaredD = square(doubled); // 64
console.log(squaredD);
double only doubles, square only squares// Each function does exactly one thing
function isEven(num) {
return num % 2 === 0;
}
function isPositive(num) {
return num > 0;
}
function isEvenAndPositive(num) {
return isEven(num) && isPositive(num);
}
console.log(isEven(4)); // true
console.log(isEven(7)); // false
console.log(isEvenAndPositive(6)); // true
console.log(isEvenAndPositive(-4)); // false — negative
isEven without caring about positivity// ✗ Bad — repeated magic number, no reuse
let total1 = 5 * 10;
let total2 = 8 * 10;
let total3 = 3 * 10;
console.log(total1, total2, total3);
// ✓ Good — single function, zero repetition
function calculateTotal(price, quantity) {
return price * quantity;
}
console.log(calculateTotal(5, 10));
console.log(calculateTotal(8, 10));
console.log(calculateTotal(3, 10));
// A function that accepts another function as input
function applyTwice(fn, value) {
return fn(fn(value));
}
const addFive = x => x + 5;
const triple = x => x * 3;
console.log(applyTwice(addFive, 10)); // addFive(addFive(10)) = 20
console.log(applyTwice(triple, 2)); // triple(triple(2)) = 18
array.map(), array.filter(), and many modern APIsThink of a factory assembly line — each machine does one job, and the output of each becomes the input of the next:
Click ▶ Preview, enter a price, and watch two small composed
functions — calculateTax() and applyDiscount() —
each do their single job and combine to produce the final result.
<!DOCTYPE html>
<html>
<head><title>Advanced Functions</title></head>
<body style="font-family:sans-serif;padding:24px;background:#f8f9fa">
<h2>Discount Calculator</h2>
<input id="priceInput" type="number" placeholder="Enter price (₹)"
style="padding:8px 12px;border:1px solid #ccc;border-radius:6px;
font-size:1rem;width:200px">
<button onclick="calculate()"
style="margin-left:8px;padding:8px 18px;background:#f7df1e;border:none;
border-radius:6px;cursor:pointer;font-weight:700;font-size:1rem">
Apply 10% Discount
</button>
<div id="output" style="margin-top:16px;line-height:2.4;font-size:0.95rem"></div>
<script>
// Each function does ONE thing
function applyDiscount(price) {
return price * 0.9; // 10% off
}
function calculateTax(price) {
return price * 1.18; // 18% GST
}
function formatPrice(amount) {
return "₹" + amount.toFixed(2);
}
function calculate() {
let raw = Number(document.getElementById("priceInput").value);
if (!raw || raw <= 0) {
document.getElementById("output").innerHTML =
"<b style='color:red'>Please enter a valid price.</b>";
return;
}
// Compose: discount first, then tax on discounted price
let discounted = applyDiscount(raw);
let withTax = calculateTax(discounted);
document.getElementById("output").innerHTML =
"<b>Original price:</b> " + formatPrice(raw) + "<br>" +
"<b>After 10% discount:</b> " + formatPrice(discounted) + "<br>" +
"<b>After 18% GST:</b> " + formatPrice(withTax);
}
</script>
</body>
</html>
function square(n) {
// return n squared
}
console.log(square(5)); // 25
console.log(square(12)); // 144
n * nconst square = n => n * nfunction calculateTax(price) {
return price * 0.10; // 10% tax
}
function addTax(price) {
return price + calculateTax(price);
}
console.log(addTax(500));
550formatBill(price) that returns a string like "Total: ₹550.00"formatBill(addTax(500))square(double(3)) where double(3) produces the input for square.
function double(x) {
return x * 2;
}
console.log(double(5));
double(5) returns 5 * 2 = 10. The parameter x receives the argument 5, and the function returns the doubled value.
Name functions by what they do, not what they are — a clear name eliminates the need for a comment:
// ✗ Vague names — what do these do?
function doStuff(x) { return x * 1.18; }
function fn2(a, b) { return a + b; }
// ✓ Clear names — self-documenting
function applyGST(price) { return price * 1.18; }
function calculateTotal(a, b) { return a + b; }
console.log(applyGST(500));
console.log(calculateTotal(300, 200));
Prefer return over console.log inside functions — returning a value makes the function reusable in expressions; printing locks it to console output only:
// ✗ Printing inside — cannot reuse the result
function badDouble(x) { console.log(x * 2); }
// ✓ Returning — result can be used anywhere
const goodDouble = x => x * 2;
let result = goodDouble(5) + goodDouble(10); // composable!
console.log(result); // 30
Avoid side effects — a pure function always returns the same output for the same input and changes nothing outside itself:
let total = 0;
// ✗ Impure — modifies external state
function addToTotal(n) { total += n; }
// ✓ Pure — no side effects, predictable
function add(a, b) { return a + b; }
console.log(add(3, 4)); // always 7
console.log(add(3, 4)); // still 7 — no surprises
Use Array.map() and Array.filter() — these are the most common higher-order functions and apply a function to every element of an array:
const prices = [100, 250, 80, 400, 60];
// map() — apply a function to every item
const discounted = prices.map(p => p * 0.9);
console.log(discounted);
// filter() — keep only items that pass a test
const expensive = prices.filter(p => p > 100);
console.log(expensive);
Two small functions are composed together — trace each step from the inside out before running.
function add(x) {
return x + 2;
}
function multiply(x) {
return x * 3;
}
let result = multiply(add(2));
console.log(result);
// JavaScript evaluates composed calls from the INSIDE OUT
// Step 1: Innermost call — add(2)
// x = 2 → return 2 + 2 = 4
// add(2) resolves to 4
// Step 2: Outer call — multiply(4)
// x = 4 → return 4 * 3 = 12
// multiply(4) resolves to 12
// Step 3: result = 12
// Output → 12
// Key takeaway:
// multiply(add(2))
// = multiply(4) ← add(2) runs first, returns 4
// = 12 ← multiply receives 4
// This is function composition — small, focused functions
// chained together to build more complex behaviour.
// Neither add() nor multiply() needed to know about the other.