Functions are the core of JavaScript. This guide removes theory clutter and focuses on how to use, how they behave, and how to avoid mistakes in real code.
Functions take inputs (parameters), process them, and return outputs.
If no return statement, the function gives back undefined.
function add(a, b) {
return a + b;
}
console.log(add(2, 3)); // 5To return more than one value, wrap them in an array or object.
function calculate(a, b) {
return { sum: a + b, product: a * b };
}
const result = calculate(2, 3);
console.log(result.sum); // 5
console.log(result.product); // 6- Primitives (numbers, strings, booleans) → passed by value.
- Objects/Arrays → passed by reference (changes inside function affect the original).
function update(arr) {
arr.push('New');
}
const list = ['Old'];
update(list);
console.log(list); // ['Old', 'New']- Once
returnexecutes, function stops running. - Use
returnto send output or exit early.
function divide(a, b) {
if (b === 0) return 'Error: cannot divide by zero';
return a / b;
}Arrow functions are short, modern alternatives to function syntax.
const add = (a, b) => a + b;-
If one parameter → parentheses optional:
x => x * x -
For one-line expressions →
returnis implied.const double = x => x * 2 -
For multiple statements → use braces
{}andreturnexplicitly.const multiply = (a, b) => { const result = a * b; return result; };
-
Arrow functions don’t have their own
this.
const person = {
name: "Brandon",
sayName: () => console.log(this.name) // 'this' not bound
};
person.sayName(); // undefinedFix:
const person = {
name: "Brandon",
sayName() { console.log(this.name); }
};
person.sayName(); // BrandonUse arrows for:
- Short, pure, inline functions.
- Array operations (
map,filter,reduce). - Callbacks.
Avoid arrows for:
- Object methods needing
this. - Constructors.
If no value or undefined is passed, the default kicks in.
function greet(name = "Guest") {
return `Hello, ${name}`;
}
console.log(greet()); // Hello, Guest
console.log(greet("Brandon")); // Hello, BrandonGather remaining arguments into an array.
function sum(...nums) {
return nums.reduce((total, n) => total + n, 0);
}
console.log(sum(2, 4, 6)); // 12function register(user = "Anonymous", ...roles) {
return `${user} registered as ${roles.join(", ")}`;
}
console.log(register("Brandon", "Admin", "Editor"));
// Brandon registered as Admin, Editor- Default applies only when value is
undefined. - Rest parameters must be last.
- Rest parameters replace
argumentsfor clarity.
A closure is when a function “remembers” variables from where it was created, even if called outside that scope.
function makeCounter() {
let count = 0;
return function() {
count++;
return count;
};
}
const counter = makeCounter();
console.log(counter()); // 1
console.log(counter()); // 2count stays private inside the closure — no external access.
function multiplier(factor) {
return function(num) {
return num * factor;
};
}
const double = multiplier(2);
console.log(double(5)); // 10Closures fix loop problems.
for (let i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 100);
}
// prints 0, 1, 2Functions that take other functions as inputs or return them.
function operate(a, b, operation) {
return operation(a, b);
}
const add = (x, y) => x + y;
console.log(operate(3, 4, add)); // 7-
map()— transforms every element.const nums = [1, 2, 3]; const doubled = nums.map(n => n * 2); console.log(doubled); // [2, 4, 6]
-
filter()— selects items that match condition.const even = nums.filter(n => n % 2 === 0); console.log(even); // [2]
-
reduce()— combines all elements into one.const sum = nums.reduce((acc, n) => acc + n, 0); console.log(sum); // 6
-
forEach()— performs action (no return).nums.forEach(n => console.log(n * 2));
function customFilter(arr, fn) {
const result = [];
for (let item of arr) {
if (fn(item)) result.push(item);
}
return result;
}
console.log(customFilter([1, 2, 3, 4], n => n > 2)); // [3,4]- Depend only on inputs.
- Produce same output every time.
- Cause no side effects.
function add(a, b) {
return a + b;
}- Depend on or modify external state.
- Side effects make results unpredictable.
let total = 0;
function addToTotal(value) {
total += value;
}Make pure by passing and returning data instead of mutating:
function addToTotalPure(total, value) {
return total + value;
}- Easier to test.
- Easier to debug.
- Predictable and reusable.
Impure:
const users = [];
function registerUser(name) {
users.push(name);
}Pure:
function registerUserPure(users, name) {
return [...users, name];
}function transformUsers(users, transformer) {
return users.map(transformer);
}
const uppercased = transformUsers(["brandon", "anna"], u => u.toUpperCase());
console.log(uppercased); // ['BRANDON', 'ANNA']function createLogger(prefix) {
return function(message) {
console.log(`[${prefix}] ${message}`);
};
}
const info = createLogger("INFO");
info("Server started"); // [INFO] Server startedfunction taxCalculator(rate) {
return function(price) {
return price + price * rate;
};
}
const kenyaTax = taxCalculator(0.16);
console.log(kenyaTax(100)); // 116const multiplyBy2 = x => x * 2;
const add10 = x => x + 10;
function compose(f, g) {
return x => f(g(x));
}
const multiplyAndAdd = compose(add10, multiplyBy2);
console.log(multiplyAndAdd(5)); // 20function average(...nums) {
const total = nums.reduce((a, b) => a + b, 0);
return total / nums.length;
}
console.log(average(10, 20, 30)); // 20- Create a function that reverses any array without mutating it.
- Write a function
compose(f, g)that chains two functions. - Use a closure to create a counter that counts only even numbers.
- Implement a custom
mapusing a for loop. - Convert an impure logging function into a pure one.
| Concept | What It Does | Common Mistake | Fix |
|---|---|---|---|
| Parameters | Inputs to functions | Forgetting defaults | Use param = value |
| Return | Output value | Missing return |
Always return result |
| Arrow Functions | Compact syntax | Wrong this |
Use normal function for methods |
| Default Params | Fallbacks | Passing null instead of undefined |
Only undefined triggers default |
| Rest Params | Collect extra args | Using before other params | Must be last |
| Closures | Remember variables | Retaining memory unnecessarily | Release refs if unused |
| HOF | Accept or return functions | Passing wrong callback | Ensure callback signature correct |
| Pure Functions | Predictable | Changing external data | Return new data |
Separate logic into:
- Pure functions for computation.
- Impure functions for input/output (fetch, database, UI).
This structure makes code predictable, testable, and clean.