javascript
exercises
exercises.js⚡javascript
/**
* ============================================
* 6.2 THE CALL STACK - EXERCISES
* ============================================
*
* Practice understanding how the call stack works,
* tracing function calls, and debugging.
*/
/**
* EXERCISE 1: Trace the Stack
* ---------------------------
* Write out the call stack at each step.
*/
console.log('=== Exercise 1: Trace the Stack ===');
function alpha() {
console.log('A');
beta();
console.log('D');
}
function beta() {
console.log('B');
gamma();
console.log('C');
}
function gamma() {
console.log('*');
}
alpha();
// YOUR ANSWER:
// Draw the stack at the point where "*" is logged:
// ┌─────────────────┐
// │ ??? │
// ├─────────────────┤
// │ ??? │
// ├─────────────────┤
// │ ??? │
// ├─────────────────┤
// │ ??? │
// └─────────────────┘
/*
* SOLUTION:
* When "*" is logged:
* ┌─────────────────┐
* │ gamma() │ ← Currently executing
* ├─────────────────┤
* │ beta() │
* ├─────────────────┤
* │ alpha() │
* ├─────────────────┤
* │ Global │
* └─────────────────┘
*
* Output order: A, B, *, C, D
*/
/**
* EXERCISE 2: Predict Output Order
* --------------------------------
* Number the console.log statements in order of execution.
*/
console.log('\n=== Exercise 2: Predict Output Order ===');
function outer() {
console.log('A'); // Order: ___
inner();
console.log('B'); // Order: ___
}
function inner() {
console.log('C'); // Order: ___
}
console.log('D'); // Order: ___
outer();
console.log('E'); // Order: ___
// YOUR ANSWER: D, A, C, B, E
/*
* SOLUTION:
* Order: D (1), A (2), C (3), B (4), E (5)
*
* D is logged first (global code)
* outer() is called, logs A
* inner() is called from outer(), logs C
* inner() returns, outer() continues, logs B
* outer() returns, global continues, logs E
*/
/**
* EXERCISE 3: Stack at Specific Point
* ------------------------------------
* What's on the stack when "CHECKPOINT" is logged?
*/
console.log('\n=== Exercise 3: Stack at Checkpoint ===');
function one() {
two();
}
function two() {
three();
}
function three() {
four();
}
function four() {
console.log('CHECKPOINT');
}
one();
// Write the stack from bottom to top:
// 1. (bottom) ___
// 2. ___
// 3. ___
// 4. ___
// 5. (top) ___
/*
* SOLUTION:
* 1. (bottom) Global
* 2. one()
* 3. two()
* 4. three()
* 5. (top) four() ← executing console.log
*/
/**
* EXERCISE 4: Return Value Path
* -----------------------------
* Trace how the return value flows back through the stack.
*/
console.log('\n=== Exercise 4: Return Value Path ===');
function multiply(x, y) {
return x * y;
}
function add(a, b) {
return a + b;
}
function calculate(n) {
const doubled = multiply(n, 2);
const final = add(doubled, 10);
return final;
}
const answer = calculate(5);
console.log('Answer:', answer);
// Trace the return values:
// calculate(5) is called
// multiply(5, 2) returns ___
// add(___, 10) returns ___
// calculate(5) returns ___
/*
* SOLUTION:
* multiply(5, 2) returns 10
* add(10, 10) returns 20
* calculate(5) returns 20
*/
/**
* EXERCISE 5: Fix the Stack Overflow
* ----------------------------------
* This function causes a stack overflow. Fix it!
*/
console.log('\n=== Exercise 5: Fix Stack Overflow ===');
// BROKEN - causes stack overflow
function countdownBroken(n) {
console.log(n);
countdownBroken(n - 1); // No stopping condition!
}
// countdownBroken(5); // DON'T RUN THIS!
// YOUR FIX:
function countdownFixed(n) {
// Add proper base case
// YOUR CODE HERE
}
// countdownFixed(5);
/*
* SOLUTION:
*/
function countdownFixedSolution(n) {
if (n < 0) return; // Base case!
console.log(n);
countdownFixedSolution(n - 1);
}
countdownFixedSolution(5);
/**
* EXERCISE 6: Recursion Stack Depth
* ---------------------------------
* How deep does the stack get for each call?
*/
console.log('\n=== Exercise 6: Recursion Stack Depth ===');
function sum(n) {
if (n <= 0) return 0;
return n + sum(n - 1);
}
// For sum(5), what's the maximum stack depth (not counting global)?
// YOUR ANSWER: ___
// Trace:
// sum(5) calls sum(4)
// sum(4) calls sum(3)
// sum(3) calls sum(2)
// sum(2) calls sum(1)
// sum(1) calls sum(0)
// sum(0) returns 0
/*
* SOLUTION:
* Maximum stack depth: 6 (sum(5), sum(4), sum(3), sum(2), sum(1), sum(0))
* Or 5 if not counting the base case that immediately returns.
*/
console.log('sum(5) =', sum(5));
/**
* EXERCISE 7: Async vs Sync Stack
* -------------------------------
* Predict the output order.
*/
console.log('\n=== Exercise 7: Async vs Sync ===');
function sync1() {
console.log('Sync 1');
}
function sync2() {
console.log('Sync 2');
}
console.log('Start');
setTimeout(() => console.log('Timeout 1'), 0);
sync1();
setTimeout(() => console.log('Timeout 2'), 0);
sync2();
console.log('End');
// YOUR PREDICTED ORDER:
// 1. ___
// 2. ___
// 3. ___
// 4. ___
// 5. ___
// 6. ___
/*
* SOLUTION:
* 1. Start
* 2. Sync 1
* 3. Sync 2
* 4. End
* 5. Timeout 1
* 6. Timeout 2
*
* Sync code runs first (in stack), then callbacks (from queue)
*/
/**
* EXERCISE 8: Read the Stack Trace
* --------------------------------
* Interpret this error stack trace.
*/
console.log('\n=== Exercise 8: Read Stack Trace ===');
function parseData(data) {
return processData(data);
}
function processData(data) {
return transformData(data);
}
function transformData(data) {
if (!data) {
throw new Error('No data provided!');
}
return data.toUpperCase();
}
try {
parseData(null);
} catch (e) {
console.log('Stack trace:');
console.log(e.stack);
}
// Questions:
// 1. Which function threw the error? ___
// 2. What was the call chain? ___ → ___ → ___
/*
* SOLUTION:
* 1. transformData threw the error
* 2. Call chain: parseData → processData → transformData
*/
/**
* EXERCISE 9: Write console.trace() Strategically
* ------------------------------------------------
* Add console.trace() calls to help debug this code.
*/
console.log('\n=== Exercise 9: Strategic console.trace() ===');
function handleRequest(req) {
validateRequest(req);
return processRequest(req);
}
function validateRequest(req) {
if (!req.id) {
// Where would you add console.trace() to see how we got here?
console.log('Invalid request!');
}
}
function processRequest(req) {
return { processed: true, id: req.id };
}
// Test with invalid request
handleRequest({});
// YOUR TASK: Add console.trace() to validateRequest to debug
/*
* SOLUTION:
*/
function validateRequestWithTrace(req) {
if (!req.id) {
console.trace('Validation failed - call stack:');
console.log('Invalid request!');
}
}
function handleRequestWithTrace(req) {
validateRequestWithTrace(req);
return processRequest(req);
}
handleRequestWithTrace({});
/**
* EXERCISE 10: Maximum Stack Depth Calculator
* -------------------------------------------
* Write a function to find the approximate max stack depth.
*/
console.log('\n=== Exercise 10: Max Stack Depth ===');
function findMaxStackDepth() {
// YOUR CODE HERE
// Hint: Use recursion with try-catch
}
/*
* SOLUTION:
*/
function findMaxStackDepthSolution() {
let depth = 0;
function dive() {
depth++;
dive();
}
try {
dive();
} catch (e) {
return depth;
}
}
console.log('Approximate max stack depth:', findMaxStackDepthSolution());
/**
* EXERCISE 11: Convert Recursive to Iterative
* -------------------------------------------
* This recursive function can overflow on large inputs.
* Convert it to iterative to fix.
*/
console.log('\n=== Exercise 11: Convert to Iterative ===');
// Recursive version (can overflow for large n)
function sumToN_recursive(n) {
if (n <= 0) return 0;
return n + sumToN_recursive(n - 1);
}
// YOUR TASK: Write an iterative version
function sumToN_iterative(n) {
// YOUR CODE HERE
}
/*
* SOLUTION:
*/
function sumToN_iterativeSolution(n) {
let total = 0;
for (let i = 1; i <= n; i++) {
total += i;
}
return total;
}
console.log('Recursive sum(10):', sumToN_recursive(10));
console.log('Iterative sum(10):', sumToN_iterativeSolution(10));
// Iterative can handle sumToN(100000) without overflow!
/**
* EXERCISE 12: Stack Frame Visualization
* --------------------------------------
* Implement a function that visualizes the call stack.
*/
console.log('\n=== Exercise 12: Stack Visualization ===');
const stackViz = [];
function visualizePush(name) {
stackViz.push(name);
console.log(`PUSH: ${name}`);
console.log('Stack:', JSON.stringify(stackViz));
}
function visualizePop() {
const name = stackViz.pop();
console.log(`POP: ${name}`);
console.log('Stack:', JSON.stringify(stackViz));
}
// YOUR TASK: Create a wrapper that auto-tracks calls
function withStackTracking(fn, name) {
return function (...args) {
// YOUR CODE HERE
};
}
/*
* SOLUTION:
*/
function withStackTrackingSolution(fn, name) {
return function (...args) {
visualizePush(name);
const result = fn.apply(this, args);
visualizePop();
return result;
};
}
// Test it
const trackedFunc = withStackTrackingSolution(() => {
console.log(' (function executing)');
return 42;
}, 'myFunction');
trackedFunc();
/**
* EXERCISE 13: Nested Callback Stack
* -----------------------------------
* Trace the stack for nested callbacks.
*/
console.log('\n=== Exercise 13: Nested Callbacks ===');
function step1(callback) {
console.log('Step 1');
callback();
}
function step2(callback) {
console.log('Step 2');
callback();
}
function step3() {
console.log('Step 3 - Top of stack!');
console.trace('Stack at step3:');
}
// What's the stack when step3 executes?
step1(() => {
step2(() => {
step3();
});
});
// YOUR ANSWER (bottom to top):
// ___
// ___
// ___
// ___
/*
* SOLUTION:
* Global
* step1
* (anonymous callback 1)
* step2
* (anonymous callback 2)
* step3 ← top
*/
/**
* EXERCISE 14: Tail Recursion
* ---------------------------
* Rewrite this function to be tail-recursive.
*/
console.log('\n=== Exercise 14: Tail Recursion ===');
// Not tail-recursive (has pending multiplication)
function factorial(n) {
if (n <= 1) return 1;
return n * factorial(n - 1); // Not in tail position
}
// YOUR TASK: Make it tail-recursive
function factorialTail(n, accumulator = 1) {
// YOUR CODE HERE
}
/*
* SOLUTION:
*/
function factorialTailSolution(n, accumulator = 1) {
if (n <= 1) return accumulator;
return factorialTailSolution(n - 1, n * accumulator); // Tail call!
}
console.log('factorial(5):', factorial(5));
console.log('factorialTail(5):', factorialTailSolution(5));
/**
* EXERCISE 15: Debug This Stack Issue
* ------------------------------------
* This code has a subtle stack-related bug. Find and fix it.
*/
console.log('\n=== Exercise 15: Debug Stack Issue ===');
let operations = 0;
function processItem(item, items, index) {
operations++;
console.log(`Processing item ${index}: ${item}`);
if (index < items.length - 1) {
// BUG: What's wrong here?
processItem(items[index + 1], items, index + 1);
}
return true;
}
const items = ['a', 'b', 'c', 'd', 'e'];
operations = 0;
processItem(items[0], items, 0);
console.log('Total operations:', operations);
// Question: This works fine for 5 items.
// What happens with 50,000 items? ___
// YOUR TASK: Rewrite to avoid potential stack overflow
function processItemsSafe(items) {
// YOUR CODE HERE
}
/*
* SOLUTION:
* Problem: For 50,000 items, we'd have 50,000 stack frames → overflow!
*
* Fix: Use iteration instead
*/
function processItemsSafeSolution(items) {
for (let i = 0; i < items.length; i++) {
console.log(`Processing item ${i}: ${items[i]}`);
}
}
console.log('\n=== Exercises Complete ===');
console.log('Review the stack traces and compare with solutions!');