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.