JavaScript Functions Deep Dive: Complete Guide for Developers
Master JavaScript functions: parameters, scope, closures, execution context, call stack, higher-order functions, callbacks, recursion, and advanced patterns with detailed examples.
JavaScript Functions Deep Dive: Complete Guide for Developers
📋 Table of Contents
- What are JavaScript Functions?
- Why Functions are Essential?
- Real-World Use Cases
- Syntax
- Internal Working
- Memory Diagram
- Data Flow Diagram
- Code Examples
- Common Mistakes
- Performance Considerations
- Interview Questions
- Cheat Sheet
- Summary
1. What are JavaScript Functions?
A function is a reusable block of code designed to perform a specific task. Functions are first-class citizens in JavaScript, meaning they can be:
- Assigned to variables
- Passed as arguments
- Returned from other functions
- Stored in data structures
Function Anatomy
┌─────────────────────────────────────────────────────────────┐
│ FUNCTION ANATOMY │
└─────────────────────────────────────────────────────────────┘
function functionName(parameters) {
↑ ↑ ↑
│ │ │
Keyword Name Parameters
// Function Body
const result = parameters * 2;
↑
│
Local Variables
return result;
↑ ↑
│ │
Keyword Return Value
}
Function Types
┌─────────────────────────────────────────────────────────────┐
│ FUNCTION TYPES IN JAVASCRIPT │
└─────────────────────────────────────────────────────────────┘
Functions
|
├─ Regular Functions
| ├─ Function Declaration
| ├─ Function Expression
| └─ Named Function Expression
|
├─ Arrow Functions (ES6)
| ├─ Single Expression
| └─ Block Body
|
├─ Generator Functions
| └─ function* name() {}
|
├─ Async Functions
| └─ async function name() {}
|
└─ Constructor Functions
└─ function Name() {}
2. Why Functions are Essential?
Functions are the building blocks of JavaScript applications.
Without Functions
// Repetitive, hard to maintain
const price1 = 100;
const tax1 = price1 * 0.18;
const total1 = price1 + tax1;
const price2 = 200;
const tax2 = price2 * 0.18;
const total2 = price2 + tax2;
const price3 = 300;
const tax3 = price3 * 0.18;
const total3 = price3 + tax3;
With Functions
// Reusable, maintainable
function calculateTotal(price) {
const tax = price * 0.18;
return price + tax;
}
const total1 = calculateTotal(100);
const total2 = calculateTotal(200);
const total3 = calculateTotal(300);
Key Benefits
- Reusability: Write once, use many times
- Modularity: Break complex problems into smaller pieces
- Abstraction: Hide implementation details
- Maintainability: Update logic in one place
- Testing: Easier to test isolated units
- Readability: Self-documenting code
3. Real-World Use Cases
1. Data Transformation
// Transform user data from API
function transformUser(apiUser) {
return {
id: apiUser.user_id,
name: `${apiUser.first_name} ${apiUser.last_name}`,
email: apiUser.email_address.toLowerCase(),
isActive: apiUser.status === 'active',
joinedDate: new Date(apiUser.created_at)
};
}
const users = apiResponse.map(transformUser);
2. Event Handling
// Handle form submission
function handleSubmit(event) {
event.preventDefault();
const formData = new FormData(event.target);
const data = Object.fromEntries(formData);
if (validateForm(data)) {
submitToAPI(data);
}
}
document.getElementById('form').addEventListener('submit', handleSubmit);
3. Business Logic
// Calculate shipping cost
function calculateShipping(weight, distance, isPriority) {
const baseRate = 5;
const weightRate = weight * 0.5;
const distanceRate = distance * 0.1;
const priorityFee = isPriority ? 10 : 0;
return baseRate + weightRate + distanceRate + priorityFee;
}
4. Higher-Order Functions
// Create specialized functions
function createMultiplier(factor) {
return function(number) {
return number * factor;
};
}
const double = createMultiplier(2);
const triple = createMultiplier(3);
console.log(double(5)); // 10
console.log(triple(5)); // 15
4. Syntax
Function Parameters
// ═══════════════════════════════════════════════════════════
// BASIC PARAMETERS
// ═══════════════════════════════════════════════════════════
function greet(name) {
return `Hello, ${name}!`;
}
// ═══════════════════════════════════════════════════════════
// DEFAULT PARAMETERS
// ═══════════════════════════════════════════════════════════
function greet(name = "Guest") {
return `Hello, ${name}!`;
}
console.log(greet()); // "Hello, Guest!"
console.log(greet("John")); // "Hello, John!"
// ═══════════════════════════════════════════════════════════
// REST PARAMETERS
// ═══════════════════════════════════════════════════════════
function sum(...numbers) {
return numbers.reduce((total, num) => total + num, 0);
}
console.log(sum(1, 2, 3, 4, 5)); // 15
// ═══════════════════════════════════════════════════════════
// DESTRUCTURING PARAMETERS
// ═══════════════════════════════════════════════════════════
function createUser({ name, email, age = 18 }) {
return { name, email, age };
}
createUser({ name: "John", email: "[email protected]" });
// Array destructuring
function getCoordinates([x, y]) {
return { x, y };
}
getCoordinates([10, 20]);
Return Values
// ═══════════════════════════════════════════════════════════
// SINGLE RETURN VALUE
// ═══════════════════════════════════════════════════════════
function add(a, b) {
return a + b;
}
// ═══════════════════════════════════════════════════════════
// MULTIPLE RETURN VALUES (using object)
// ═══════════════════════════════════════════════════════════
function calculate(a, b) {
return {
sum: a + b,
difference: a - b,
product: a * b,
quotient: a / b
};
}
const { sum, product } = calculate(10, 5);
// ═══════════════════════════════════════════════════════════
// MULTIPLE RETURN VALUES (using array)
// ═══════════════════════════════════════════════════════════
function getMinMax(numbers) {
return [Math.min(...numbers), Math.max(...numbers)];
}
const [min, max] = getMinMax([1, 5, 3, 9, 2]);
// ═══════════════════════════════════════════════════════════
// EARLY RETURN
// ═══════════════════════════════════════════════════════════
function processUser(user) {
if (!user) return null;
if (!user.isActive) return null;
return {
id: user.id,
name: user.name
};
}
Higher-Order Functions
// ═══════════════════════════════════════════════════════════
// FUNCTION AS PARAMETER
// ═══════════════════════════════════════════════════════════
function execute(callback) {
console.log("Before callback");
callback();
console.log("After callback");
}
execute(() => console.log("Callback executed"));
// ═══════════════════════════════════════════════════════════
// FUNCTION RETURNING FUNCTION
// ═══════════════════════════════════════════════════════════
function createGreeter(greeting) {
return function(name) {
return `${greeting}, ${name}!`;
};
}
const sayHello = createGreeter("Hello");
const sayHi = createGreeter("Hi");
console.log(sayHello("John")); // "Hello, John!"
console.log(sayHi("Jane")); // "Hi, Jane!"
5. Internal Working
Function Execution Context
┌─────────────────────────────────────────────────────────────┐
│ FUNCTION EXECUTION CONTEXT │
└─────────────────────────────────────────────────────────────┘
Code:
─────
function multiply(a, b) {
const result = a * b;
return result;
}
multiply(3, 4);
Execution Process:
──────────────────
Step 1: Function Call
──────────────────────
Global Execution Context
↓
Call multiply(3, 4)
↓
Create New Execution Context
Step 2: Creation Phase
──────────────────────
Execution Context for multiply()
├─ Variable Environment
│ ├─ a: undefined
│ ├─ b: undefined
│ └─ result: undefined
│
├─ Scope Chain
│ └─ Reference to Global Scope
│
└─ this binding
└─ undefined (strict mode) or global object
Step 3: Execution Phase
───────────────────────
Execution Context for multiply()
├─ a: 3 ← Assign arguments
├─ b: 4 ← Assign arguments
└─ result: 12 ← Execute code
Step 4: Return
──────────────
Return value: 12
↓
Pop execution context from stack
↓
Return to Global Execution Context
Call Stack
┌─────────────────────────────────────────────────────────────┐
│ CALL STACK VISUALIZATION │
└─────────────────────────────────────────────────────────────┘
Code:
─────
function first() {
console.log("First");
second();
}
function second() {
console.log("Second");
third();
}
function third() {
console.log("Third");
}
first();
Call Stack Evolution:
─────────────────────
Initial:
┌──────────────┐
│ Global │
└──────────────┘
After first() called:
┌──────────────┐
│ first() │
├──────────────┤
│ Global │
└──────────────┘
After second() called:
┌──────────────┐
│ second() │
├──────────────┤
│ first() │
├──────────────┤
│ Global │
└──────────────┘
After third() called:
┌──────────────┐
│ third() │ ← Top of stack
├──────────────┤
│ second() │
├──────────────┤
│ first() │
├──────────────┤
│ Global │
└──────────────┘
After third() returns:
┌──────────────┐
│ second() │
├──────────────┤
│ first() │
├──────────────┤
│ Global │
└──────────────┘
After second() returns:
┌──────────────┐
│ first() │
├──────────────┤
│ Global │
└──────────────┘
After first() returns:
┌──────────────┐
│ Global │
└──────────────┘
Closure Mechanism
┌─────────────────────────────────────────────────────────────┐
│ CLOSURE INTERNAL WORKING │
└─────────────────────────────────────────────────────────────┘
Code:
─────
function createCounter() {
let count = 0;
return function() {
count++;
return count;
};
}
const counter = createCounter();
Memory Structure:
─────────────────
Step 1: createCounter() executed
─────────────────────────────────
Global Memory:
┌─────────────────────────────────┐
│ counter: undefined │
└─────────────────────────────────┘
createCounter() Execution Context:
┌─────────────────────────────────┐
│ count: 0 │
│ return: [Function] │
└─────────────────────────────────┘
Step 2: After createCounter() returns
──────────────────────────────────────
Global Memory:
┌─────────────────────────────────┐
│ counter: [Function] │
│ ↓ │
│ [[Scope]]: { │
│ count: 0 │ ← Closure!
│ } │
└─────────────────────────────────┘
Step 3: counter() called
────────────────────────
counter() Execution Context:
┌─────────────────────────────────┐
│ Access count from closure │
│ count: 0 → 1 │
│ return: 1 │
└─────────────────────────────────┘
Closure persists:
┌─────────────────────────────────┐
│ counter: [Function] │
│ [[Scope]]: { │
│ count: 1 │ ← Updated!
│ } │
└─────────────────────────────────┘
6. Memory Diagram
┌─────────────────────────────────────────────────────────────┐
│ FUNCTION MEMORY ALLOCATION │
└─────────────────────────────────────────────────────────────┘
Code:
─────
function outer(x) {
const y = 10;
function inner(z) {
return x + y + z;
}
return inner;
}
const myFunc = outer(5);
const result = myFunc(3);
Memory Layout:
──────────────
Heap (Function Objects):
┌─────────────────────────────────────┐
│ outer: [Function Object] │
│ ├─ name: "outer" │
│ ├─ length: 1 │
│ └─ [[Code]]: ... │
└─────────────────────────────────────┘
After outer(5) called:
┌─────────────────────────────────────┐
│ inner: [Function Object] │
│ ├─ name: "inner" │
│ ├─ length: 1 │
│ ├─ [[Code]]: ... │
│ └─ [[Scope]]: { │
│ x: 5, │ ← Closure
│ y: 10 │
│ } │
└─────────────────────────────────────┘
Stack (Execution Contexts):
┌─────────────────────────────────────┐
│ myFunc(3) Execution Context │
│ ├─ z: 3 │
│ ├─ Access x: 5 (from closure) │
│ ├─ Access y: 10 (from closure) │
│ └─ result: 18 │
└─────────────────────────────────────┘
┌─────────────────────────────────────┐
│ Global Execution Context │
│ ├─ myFunc: reference to inner │
│ └─ result: 18 │
└─────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ RECURSIVE FUNCTION MEMORY │
└─────────────────────────────────────────────────────────────┘
Code:
─────
function factorial(n) {
if (n <= 1) return 1;
return n * factorial(n - 1);
}
factorial(4);
Call Stack Growth:
──────────────────
factorial(4):
┌──────────────┐
│ n: 4 │
│ waiting... │
└──────────────┘
┌──────────────┐
│ Global │
└──────────────┘
factorial(3):
┌──────────────┐
│ n: 3 │
│ waiting... │
├──────────────┤
│ n: 4 │
│ waiting... │
├──────────────┤
│ Global │
└──────────────┘
factorial(2):
┌──────────────┐
│ n: 2 │
│ waiting... │
├──────────────┤
│ n: 3 │
│ waiting... │
├──────────────┤
│ n: 4 │
│ waiting... │
├──────────────┤
│ Global │
└──────────────┘
factorial(1):
┌──────────────┐
│ n: 1 │
│ return: 1 │ ← Base case
├──────────────┤
│ n: 2 │
│ return: 2 │ ← 2 * 1
├──────────────┤
│ n: 3 │
│ return: 6 │ ← 3 * 2
├──────────────┤
│ n: 4 │
│ return: 24 │ ← 4 * 6
├──────────────┤
│ Global │
└──────────────┘
7. Data Flow Diagram
┌─────────────────────────────────────────────────────────────┐
│ FUNCTION CALL FLOW │
└─────────────────────────────────────────────────────────────┘
Function Call
↓
┌─────────────────┐
│ Create │
│ Execution │
│ Context │
└─────────────────┘
↓
┌─────────────────┐
│ Creation Phase │
│ - Allocate vars │
│ - Setup scope │
│ - Bind 'this' │
└─────────────────┘
↓
┌─────────────────┐
│ Execution Phase │
│ - Assign values │
│ - Execute code │
└─────────────────┘
↓
┌─────────────────┐
│ Return Value │
└─────────────────┘
↓
┌─────────────────┐
│ Destroy Context │
│ (Pop from stack)│
└─────────────────┘
↓
Return to Caller
┌─────────────────────────────────────────────────────────────┐
│ CLOSURE DATA FLOW │
└─────────────────────────────────────────────────────────────┘
Outer Function Called
↓
┌─────────────────────────┐
│ Create Outer Context │
│ - Local variables │
└─────────────────────────┘
↓
┌─────────────────────────┐
│ Inner Function Created │
│ - Captures outer scope │
│ - Forms closure │
└─────────────────────────┘
↓
┌─────────────────────────┐
│ Outer Function Returns │
│ - Context destroyed │
│ - BUT closure persists │
└─────────────────────────┘
↓
┌─────────────────────────┐
│ Inner Function Called │
│ - Access closed vars │
└─────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ CALLBACK FLOW │
└─────────────────────────────────────────────────────────────┘
Higher-Order Function Called
↓
┌─────────────────────────┐
│ Receive Callback │
└─────────────────────────┘
↓
┌─────────────────────────┐
│ Execute Logic │
└─────────────────────────┘
↓
┌─────────────────────────┐
│ Call Callback │
│ - Pass data │
└─────────────────────────┘
↓
┌─────────────────────────┐
│ Callback Executes │
│ - Process data │
└─────────────────────────┘
↓
┌─────────────────────────┐
│ Return to HOF │
└─────────────────────────┘
↓
Continue Execution
8. Code Examples
Example 1: Pure Functions
// ✅ Pure function - same input, same output, no side effects
function add(a, b) {
return a + b;
}
console.log(add(2, 3)); // Always 5
console.log(add(2, 3)); // Always 5
// ❌ Impure function - depends on external state
let total = 0;
function addToTotal(value) {
total += value; // Side effect: modifies external variable
return total;
}
console.log(addToTotal(5)); // 5
console.log(addToTotal(5)); // 10 (different result!)
// ✅ Pure version
function addToTotal(currentTotal, value) {
return currentTotal + value;
}
let total = 0;
total = addToTotal(total, 5); // 5
total = addToTotal(total, 5); // 10
Example 2: Closures for Data Privacy
// Module pattern using closure
function createBankAccount(initialBalance) {
let balance = initialBalance; // Private variable
return {
deposit(amount) {
if (amount > 0) {
balance += amount;
return `Deposited $${amount}. New balance: $${balance}`;
}
return "Invalid amount";
},
withdraw(amount) {
if (amount > 0 && amount <= balance) {
balance -= amount;
return `Withdrew $${amount}. New balance: $${balance}`;
}
return "Invalid amount or insufficient funds";
},
getBalance() {
return `Current balance: $${balance}`;
}
};
}
const myAccount = createBankAccount(1000);
console.log(myAccount.deposit(500)); // "Deposited $500. New balance: $1500"
console.log(myAccount.withdraw(200)); // "Withdrew $200. New balance: $1300"
console.log(myAccount.getBalance()); // "Current balance: $1300"
console.log(myAccount.balance); // undefined (private!)
Example 3: Higher-Order Functions
// Function composition
function compose(...functions) {
return function(value) {
return functions.reduceRight((acc, fn) => fn(acc), value);
};
}
const addTax = price => price * 1.18;
const addShipping = price => price + 10;
const formatPrice = price => `$${price.toFixed(2)}`;
const calculateFinalPrice = compose(formatPrice, addShipping, addTax);
console.log(calculateFinalPrice(100)); // "$128.00"
// Partial application
function multiply(a) {
return function(b) {
return a * b;
};
}
const double = multiply(2);
const triple = multiply(3);
console.log(double(5)); // 10
console.log(triple(5)); // 15
// Currying
function curry(fn) {
return function curried(...args) {
if (args.length >= fn.length) {
return fn.apply(this, args);
}
return function(...nextArgs) {
return curried.apply(this, args.concat(nextArgs));
};
};
}
function sum(a, b, c) {
return a + b + c;
}
const curriedSum = curry(sum);
console.log(curriedSum(1)(2)(3)); // 6
console.log(curriedSum(1, 2)(3)); // 6
console.log(curriedSum(1)(2, 3)); // 6
Example 4: Recursive Functions
// Factorial
function factorial(n) {
if (n <= 1) return 1;
return n * factorial(n - 1);
}
console.log(factorial(5)); // 120
// Fibonacci
function fibonacci(n) {
if (n <= 1) return n;
return fibonacci(n - 1) + fibonacci(n - 2);
}
console.log(fibonacci(7)); // 13
// Optimized with memoization
function memoize(fn) {
const cache = {};
return function(...args) {
const key = JSON.stringify(args);
if (key in cache) {
return cache[key];
}
const result = fn.apply(this, args);
cache[key] = result;
return result;
};
}
const fastFibonacci = memoize(fibonacci);
console.log(fastFibonacci(40)); // Much faster!
// Deep object traversal
function deepClone(obj) {
if (obj === null || typeof obj !== 'object') {
return obj;
}
if (Array.isArray(obj)) {
return obj.map(item => deepClone(item));
}
const cloned = {};
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
cloned[key] = deepClone(obj[key]);
}
}
return cloned;
}
const original = { a: 1, b: { c: 2, d: [3, 4] } };
const copy = deepClone(original);
copy.b.c = 99;
console.log(original.b.c); // 2 (unchanged)
Example 5: Async Functions with Callbacks
// Callback pattern
function fetchUser(userId, callback) {
setTimeout(() => {
const user = { id: userId, name: "John Doe" };
callback(null, user);
}, 1000);
}
fetchUser(1, (error, user) => {
if (error) {
console.error("Error:", error);
} else {
console.log("User:", user);
}
});
// Callback hell (pyramid of doom)
fetchUser(1, (error, user) => {
if (error) return console.error(error);
fetchPosts(user.id, (error, posts) => {
if (error) return console.error(error);
fetchComments(posts[0].id, (error, comments) => {
if (error) return console.error(error);
console.log(comments);
});
});
});
// Better: Promise-based approach
function fetchUserPromise(userId) {
return new Promise((resolve, reject) => {
setTimeout(() => {
const user = { id: userId, name: "John Doe" };
resolve(user);
}, 1000);
});
}
fetchUserPromise(1)
.then(user => fetchPosts(user.id))
.then(posts => fetchComments(posts[0].id))
.then(comments => console.log(comments))
.catch(error => console.error(error));
9. Common Mistakes
Mistake 1: Forgetting to Return
// ❌ Wrong: No return statement
function add(a, b) {
a + b; // Result is calculated but not returned
}
console.log(add(2, 3)); // undefined
// ✅ Correct: Return the result
function add(a, b) {
return a + b;
}
console.log(add(2, 3)); // 5
Mistake 2: Infinite Recursion
// ❌ Wrong: No base case
function countdown(n) {
console.log(n);
countdown(n - 1); // Never stops!
}
// countdown(5); // RangeError: Maximum call stack size exceeded
// ✅ Correct: Include base case
function countdown(n) {
if (n <= 0) return; // Base case
console.log(n);
countdown(n - 1);
}
countdown(5); // 5, 4, 3, 2, 1
Mistake 3: Modifying Parameters
// ❌ Wrong: Mutating object parameter
function updateUser(user) {
user.name = "Updated"; // Modifies original object
return user;
}
const original = { name: "John" };
const updated = updateUser(original);
console.log(original.name); // "Updated" (mutated!)
// ✅ Correct: Create new object
function updateUser(user) {
return { ...user, name: "Updated" };
}
const original = { name: "John" };
const updated = updateUser(original);
console.log(original.name); // "John" (unchanged)
Mistake 4: Creating Functions in Loops
// ❌ Wrong: Creating function in loop
const functions = [];
for (var i = 0; i < 3; i++) {
functions.push(function() {
console.log(i);
});
}
functions[0](); // 3 (not 0!)
functions[1](); // 3 (not 1!)
functions[2](); // 3 (not 2!)
// ✅ Correct: Use let or IIFE
const functions = [];
for (let i = 0; i < 3; i++) {
functions.push(function() {
console.log(i);
});
}
functions[0](); // 0
functions[1](); // 1
functions[2](); // 2
Mistake 5: Losing 'this' Context
const obj = {
name: "John",
// ❌ Wrong: Arrow function loses 'this'
greet: () => {
console.log(`Hello, ${this.name}`); // 'this' is not obj
},
// ✅ Correct: Regular function
greetCorrect: function() {
console.log(`Hello, ${this.name}`);
}
};
obj.greet(); // "Hello, undefined"
obj.greetCorrect(); // "Hello, John"
10. Performance Considerations
1. Function Call Overhead
// ❌ Slow: Excessive function calls
function processArray(arr) {
return arr.map(x => x * 2)
.filter(x => x > 10)
.map(x => x + 1);
}
// ✅ Fast: Single pass
function processArray(arr) {
const result = [];
for (const x of arr) {
const doubled = x * 2;
if (doubled > 10) {
result.push(doubled + 1);
}
}
return result;
}
2. Memoization for Expensive Calculations
// Without memoization
function expensiveCalculation(n) {
console.log("Calculating...");
return n * n;
}
// With memoization
function memoize(fn) {
const cache = new Map();
return function(...args) {
const key = JSON.stringify(args);
if (cache.has(key)) {
return cache.get(key);
}
const result = fn.apply(this, args);
cache.set(key, result);
return result;
};
}
const fastCalculation = memoize(expensiveCalculation);
fastCalculation(5); // "Calculating..." 25
fastCalculation(5); // 25 (from cache, no log)
3. Avoid Creating Functions in Hot Paths
// ❌ Slow: Creating function on every render
function Component() {
return (
<button onClick={() => handleClick()}>
Click me
</button>
);
}
// ✅ Fast: Define function once
function Component() {
const handleClick = useCallback(() => {
// handle click
}, []);
return <button onClick={handleClick}>Click me</button>;
}
11. Interview Questions
Q1: What are first-class functions?
Answer: Functions are first-class citizens in JavaScript, meaning they can be:
- Assigned to variables
- Passed as arguments to other functions
- Returned from functions
- Stored in data structures
// Assigned to variable
const greet = function() { return "Hello"; };
// Passed as argument
function execute(fn) { return fn(); }
// Returned from function
function createGreeter() {
return function() { return "Hello"; };
}
// Stored in array
const functions = [greet, execute, createGreeter];
Q2: What is a closure?
Answer: A closure is a function bundled together with references to its surrounding state (lexical environment). Closures allow functions to access variables from an outer function even after the outer function has returned.
function createCounter() {
let count = 0; // Private variable
return function() {
count++;
return count;
};
}
const counter = createCounter();
console.log(counter()); // 1
console.log(counter()); // 2
// 'count' is preserved in closure
Q3: What is a higher-order function?
Answer: A higher-order function is a function that either:
- Takes one or more functions as arguments, OR
- Returns a function as its result
// Takes function as argument
function map(array, fn) {
const result = [];
for (const item of array) {
result.push(fn(item));
}
return result;
}
// Returns function
function multiplier(factor) {
return function(number) {
return number * factor;
};
}
Q4: What is the difference between parameters and arguments?
Answer:
- Parameters: Variables listed in the function definition
- Arguments: Actual values passed to the function when called
function add(a, b) { // a, b are parameters
return a + b;
}
add(5, 3); // 5, 3 are arguments
Q5: Explain function hoisting
Answer: Function declarations are hoisted to the top of their scope, meaning they can be called before they appear in the code. Function expressions are not hoisted.
// ✅ Works: Function declaration is hoisted
greet();
function greet() {
console.log("Hello");
}
// ❌ Error: Function expression is not hoisted
greet(); // ReferenceError
const greet = function() {
console.log("Hello");
};
Q6: What is recursion and when should you use it?
Answer: Recursion is when a function calls itself. Use recursion for:
- Tree/graph traversal
- Divide and conquer algorithms
- Problems with recursive structure (factorial, Fibonacci)
Always include a base case to prevent infinite recursion.
function factorial(n) {
if (n <= 1) return 1; // Base case
return n * factorial(n - 1); // Recursive case
}
12. Cheat Sheet
// ═══════════════════════════════════════════════════════════
// FUNCTIONS QUICK REFERENCE
// ═══════════════════════════════════════════════════════════
// FUNCTION TYPES
// ──────────────
function declaration() {} // Declaration
const expression = function() {}; // Expression
const arrow = () => {}; // Arrow
const named = function name() {}; // Named expression
(function() {})(); // IIFE
// PARAMETERS
// ──────────
function basic(a, b) {} // Basic
function defaults(a = 1) {} // Default
function rest(...args) {} // Rest
function destructure({ x, y }) {} // Destructuring
// RETURN
// ──────
return value; // Single value
return { a, b }; // Multiple (object)
return [a, b]; // Multiple (array)
// HIGHER-ORDER FUNCTIONS
// ──────────────────────
function hof(callback) { callback(); } // Takes function
function hof() { return function() {}; } // Returns function
// CLOSURES
// ────────
function outer() {
const x = 1;
return function inner() {
return x; // Accesses outer variable
};
}
// RECURSION
// ─────────
function recursive(n) {
if (n <= 0) return; // Base case
recursive(n - 1); // Recursive call
}
// PURE FUNCTIONS
// ──────────────
function pure(a, b) {
return a + b; // No side effects
}
// BEST PRACTICES
// ──────────────
✓ Use descriptive names
✓ Keep functions small (single responsibility)
✓ Avoid side effects when possible
✓ Return early to reduce nesting
✓ Use default parameters
✓ Prefer pure functions
✓ Document complex logic
✗ Don't modify parameters
✗ Don't create functions in loops
✗ Don't forget base case in recursion
✗ Don't nest too deeply (max 3 levels)
// PERFORMANCE TIPS
// ────────────────
• Memoize expensive calculations
• Avoid creating functions in hot paths
• Use single-pass operations
• Consider tail call optimization
• Profile before optimizing
13. Summary
Key Takeaways
✅ Functions are first-class citizens in JavaScript
✅ Closures preserve access to outer scope variables
✅ Higher-order functions enable functional programming
✅ Pure functions are predictable and testable
✅ Recursion solves problems with recursive structure
✅ Execution context manages function execution
✅ Call stack tracks function calls
✅ Parameters vs arguments are different concepts
✅ Hoisting affects function declarations
✅ Memoization optimizes repeated calculations
Best Practices
- Write pure functions when possible
- Use closures for data privacy
- Leverage higher-order functions for abstraction
- Keep functions small and focused
- Use descriptive names that explain purpose
- Return early to reduce nesting
- Include base cases in recursive functions
- Avoid side effects unless necessary
- Document complex logic with comments
- Profile before optimizing performance
Functions are the foundation of JavaScript. Mastering them enables you to write clean, maintainable, and efficient code.