javascript

examples

examples.js
/**
 * ============================================
 * 6.2 THE CALL STACK - EXAMPLES
 * ============================================
 */

/**
 * EXAMPLE 1: Basic Call Stack
 * ---------------------------
 * Watch functions push and pop from the stack
 */

console.log('=== Example 1: Basic Call Stack ===');

function first() {
  console.log('1. Entering first()');
  second();
  console.log('4. Exiting first()');
}

function second() {
  console.log('2. Entering second()');
  third();
  console.log('3. Exiting second()');
}

function third() {
  console.log('   In third() - top of stack!');
}

first();
console.log('5. Back in global scope');

/*
 * Stack evolution:
 * [Global] → [first, Global] → [second, first, Global]
 * → [third, second, first, Global] → [second, first, Global]
 * → [first, Global] → [Global]
 */

/**
 * EXAMPLE 2: Return Values Through the Stack
 * ------------------------------------------
 * Values bubble up as functions pop
 */

console.log('\n=== Example 2: Return Values ===');

function addTwo(x) {
  console.log(`  addTwo(${x}) called`);
  return x + 2;
}

function multiplyByThree(x) {
  console.log(`  multiplyByThree(${x}) called`);
  const result = addTwo(x);
  return result * 3;
}

function calculate(x) {
  console.log(`calculate(${x}) called`);
  return multiplyByThree(x);
}

const result = calculate(5);
console.log('Result:', result);
// calculate(5) → multiplyByThree(5) → addTwo(5) → 7
// 7 returns to multiplyByThree → 7 * 3 = 21
// 21 returns to calculate → 21 returns to global

/**
 * EXAMPLE 3: Using console.trace()
 * --------------------------------
 * Built-in way to see the call stack
 */

console.log('\n=== Example 3: console.trace() ===');

function level1() {
  level2();
}

function level2() {
  level3();
}

function level3() {
  console.trace('Call stack at level3:');
}

level1();

/**
 * EXAMPLE 4: Stack with Arguments
 * -------------------------------
 * Each frame has its own arguments
 */

console.log('\n=== Example 4: Stack with Arguments ===');

function greet(greeting) {
  console.log(`greet() received: "${greeting}"`);
  personalize('Alice');
}

function personalize(name) {
  console.log(`personalize() received: "${name}"`);
  // Each function has its own arguments
}

greet('Hello');

/**
 * EXAMPLE 5: Understanding Stack Overflow
 * ---------------------------------------
 * Demonstration of what causes overflow
 */

console.log('\n=== Example 5: Stack Overflow Demo ===');

// This would cause stack overflow - DON'T UNCOMMENT!
// function infiniteRecursion() {
//     infiniteRecursion();
// }
// infiniteRecursion();

// Safe demonstration - counting recursion depth
let depth = 0;

function measureStackDepth() {
  depth++;
  try {
    measureStackDepth();
  } catch (e) {
    // Stack overflow caught
  }
}

measureStackDepth();
console.log(`Approximate stack depth limit: ${depth}`);

/**
 * EXAMPLE 6: Proper Recursion with Base Case
 * ------------------------------------------
 * Recursion that doesn't overflow
 */

console.log('\n=== Example 6: Proper Recursion ===');

function countdown(n) {
  console.log(`  countdown(${n})`);

  // Base case - stops the recursion
  if (n <= 0) {
    console.log('  Blast off! 🚀');
    return;
  }

  // Recursive case
  countdown(n - 1);
}

countdown(5);

/**
 * EXAMPLE 7: Stack Trace from Error
 * ---------------------------------
 * Reading error stack traces
 */

console.log('\n=== Example 7: Error Stack Trace ===');

function controller() {
  service();
}

function service() {
  repository();
}

function repository() {
  database();
}

function database() {
  // Simulate an error
  throw new Error('Connection failed!');
}

try {
  controller();
} catch (e) {
  console.log('Error caught!');
  console.log('Stack trace:');
  console.log(e.stack);
}

/**
 * EXAMPLE 8: Multiple Call Stacks (Sort of)
 * -----------------------------------------
 * Each call creates a new stack frame, even for the same function
 */

console.log('\n=== Example 8: Same Function, Different Frames ===');

function process(id) {
  console.log(`Processing ${id}`);
  if (id < 3) {
    process(id + 1);
  }
  console.log(`Finished ${id}`);
}

process(1);
// Each call to process() is a separate frame on the stack

/**
 * EXAMPLE 9: Blocking the Call Stack
 * -----------------------------------
 * Demonstration of stack blocking
 */

console.log('\n=== Example 9: Blocking Demo ===');

function quickOperation() {
  console.log('Quick operation done');
}

function slowOperation() {
  console.log('Starting slow operation...');

  // This blocks - DON'T do this in real code!
  const start = Date.now();
  while (Date.now() - start < 100) {
    // Busy waiting
  }

  console.log('Slow operation done (blocked for 100ms)');
}

console.log('Before slow operation');
slowOperation();
quickOperation(); // Has to wait for slowOperation
console.log('After all operations');

/**
 * EXAMPLE 10: Non-Blocking Alternative
 * ------------------------------------
 * Using async patterns to avoid blocking
 */

console.log('\n=== Example 10: Non-Blocking ===');

function asyncOperation(callback) {
  console.log('Starting async operation...');

  // This doesn't block - callback goes to task queue
  setTimeout(() => {
    console.log('Async operation completed!');
    callback();
  }, 100);

  console.log('Async operation scheduled (not blocking)');
}

console.log('Before async operation');
asyncOperation(() => console.log('Callback executed'));
console.log('After starting async operation');
// Note the order of outputs!

/**
 * EXAMPLE 11: LIFO Demonstrated
 * -----------------------------
 * Last In, First Out behavior
 */

console.log('\n=== Example 11: LIFO Behavior ===');

function a() {
  console.log('a() starting');
  b();
  console.log('a() ending - was waiting for b()');
}

function b() {
  console.log('  b() starting');
  c();
  console.log('  b() ending - was waiting for c()');
}

function c() {
  console.log('    c() executing - top of stack');
  // c() finishes first (Last In, First Out)
}

a();

/**
 * EXAMPLE 12: Visualizing with Custom Stack
 * -----------------------------------------
 * Manual stack simulation
 */

console.log('\n=== Example 12: Manual Stack Visualization ===');

const callStack = [];

function pushToStack(name) {
  callStack.push(name);
  console.log(`PUSH: ${name}`);
  console.log(`Stack: [${callStack.join(', ')}]`);
}

function popFromStack() {
  const name = callStack.pop();
  console.log(`POP: ${name}`);
  console.log(`Stack: [${callStack.join(', ')}]`);
  return name;
}

pushToStack('global');
pushToStack('funcA');
pushToStack('funcB');
pushToStack('funcC');
popFromStack();
popFromStack();
popFromStack();
popFromStack();

/**
 * EXAMPLE 13: Factorial with Stack Visualization
 * -----------------------------------------------
 * See how recursion uses the stack
 */

console.log('\n=== Example 13: Factorial Stack ===');

function factorialVerbose(n, indent = '') {
  console.log(`${indent}factorial(${n}) called`);

  if (n <= 1) {
    console.log(`${indent}Base case: returning 1`);
    return 1;
  }

  const result = n * factorialVerbose(n - 1, indent + '  ');
  console.log(`${indent}factorial(${n}) returning ${result}`);
  return result;
}

console.log('Result:', factorialVerbose(5));

/**
 * EXAMPLE 14: Mutual Recursion
 * ----------------------------
 * Two functions calling each other
 */

console.log('\n=== Example 14: Mutual Recursion ===');

function isEven(n) {
  if (n === 0) return true;
  return isOdd(n - 1);
}

function isOdd(n) {
  if (n === 0) return false;
  return isEven(n - 1);
}

console.log('isEven(4):', isEven(4)); // true
console.log('isOdd(5):', isOdd(5)); // true

// Stack for isEven(4):
// isEven(4) → isOdd(3) → isEven(2) → isOdd(1) → isEven(0) → true

/**
 * EXAMPLE 15: Stack and Event Loop Interaction
 * --------------------------------------------
 * How async code interacts with the stack
 */

console.log('\n=== Example 15: Stack and Event Loop ===');

function syncTask() {
  console.log('2. Sync task executing (in stack)');
}

function asyncTask() {
  console.log('4. Async task executing (from queue)');
}

console.log('1. Start (in stack)');

// This goes to the task queue, not the stack
setTimeout(asyncTask, 0);

syncTask();

console.log('3. End (in stack)');

// Output order: 1, 2, 3, 4
// Async task waits for stack to be empty

/**
 * EXAMPLE 16: Creating a Stack Trace Manually
 * -------------------------------------------
 * How to capture and use stack information
 */

console.log('\n=== Example 16: Manual Stack Trace ===');

function captureStack() {
  const stack = new Error().stack;
  return stack.split('\n').slice(1).join('\n');
}

function innerFunction() {
  console.log('Stack at innerFunction:');
  console.log(captureStack());
}

function middleFunction() {
  innerFunction();
}

function outerFunction() {
  middleFunction();
}

outerFunction();

/**
 * EXAMPLE 17: Stack with Try-Catch
 * --------------------------------
 * Error handling and the stack
 */

console.log('\n=== Example 17: Try-Catch and Stack ===');

function layer3() {
  throw new Error('Error at layer 3');
}

function layer2() {
  layer3();
}

function layer1() {
  try {
    layer2();
  } catch (e) {
    console.log('Caught at layer1!');
    console.log('Error originated at:', e.stack.split('\n')[1].trim());
  }
}

layer1();

/**
 * EXAMPLE 18: Performance Considerations
 * --------------------------------------
 * Deep stacks have overhead
 */

console.log('\n=== Example 18: Performance ===');

// Iterative (constant stack space)
function sumIterative(n) {
  let total = 0;
  for (let i = 1; i <= n; i++) {
    total += i;
  }
  return total;
}

// Recursive (O(n) stack space)
function sumRecursive(n) {
  if (n <= 0) return 0;
  return n + sumRecursive(n - 1);
}

console.time('Iterative');
console.log('Iterative sum(1000):', sumIterative(1000));
console.timeEnd('Iterative');

console.time('Recursive');
console.log('Recursive sum(1000):', sumRecursive(1000));
console.timeEnd('Recursive');

console.log('\n=== End of Examples ===');
Examples - JavaScript Tutorial | DeepML