javascript

exercises

exercises.js
/**
 * ============================================
 * 6.1 EXECUTION CONTEXT - EXERCISES
 * ============================================
 *
 * Practice understanding execution contexts,
 * the creation phase, and the execution phase.
 */

/**
 * EXERCISE 1: Predict the Output
 * ------------------------------
 * Without running the code, predict what will be logged.
 * Then run it to check your answer.
 */

console.log('=== Exercise 1: Predict the Output ===');

console.log(a);
var a = 5;
console.log(a);

// YOUR PREDICTION:
// First console.log outputs: ___
// Second console.log outputs: ___

/*
 * SOLUTION:
 * First console.log outputs: undefined
 * Second console.log outputs: 5
 *
 * Explanation:
 * - Creation phase: 'a' is hoisted and set to undefined
 * - Execution phase: First log runs (a is undefined)
 * - Then a = 5 is assigned
 * - Second log runs (a is now 5)
 */

/**
 * EXERCISE 2: Function Hoisting
 * -----------------------------
 * Predict the output and explain why.
 */

console.log('\n=== Exercise 2: Function Hoisting ===');

function exercise2() {
  console.log(typeof foo);
  console.log(typeof bar);

  var foo = 'foo value';
  function bar() {
    return 'bar function';
  }

  console.log(typeof foo);
  console.log(typeof bar);
}

exercise2();

// YOUR PREDICTION:
// Line 1 (typeof foo): ___
// Line 2 (typeof bar): ___
// Line 3 (typeof foo): ___
// Line 4 (typeof bar): ___

/*
 * SOLUTION:
 * Line 1: "undefined" (var hoisted, value not assigned yet)
 * Line 2: "function" (function declarations fully hoisted)
 * Line 3: "string" (foo now has its value)
 * Line 4: "function" (still a function)
 */

/**
 * EXERCISE 3: Scope Chain
 * -----------------------
 * Predict the output of each console.log.
 */

console.log('\n=== Exercise 3: Scope Chain ===');

var x = 10;

function outer() {
  var x = 20;

  function inner() {
    var x = 30;
    console.log('inner x:', x);
  }

  inner();
  console.log('outer x:', x);
}

outer();
console.log('global x:', x);

// YOUR PREDICTION:
// inner x: ___
// outer x: ___
// global x: ___

/*
 * SOLUTION:
 * inner x: 30
 * outer x: 20
 * global x: 10
 *
 * Each function has its own 'x' in its execution context.
 */

/**
 * EXERCISE 4: Missing Variable
 * ----------------------------
 * Predict what happens at each console.log.
 */

console.log('\n=== Exercise 4: Missing Variable ===');

var y = 100;

function exercise4() {
  console.log('y inside function:', y);

  function inner() {
    console.log('y in inner:', y);
  }
  inner();
}

exercise4();

// YOUR PREDICTION:
// y inside function: ___
// y in inner: ___

/*
 * SOLUTION:
 * y inside function: 100
 * y in inner: 100
 *
 * When 'y' isn't found locally, JavaScript looks up the scope chain.
 */

/**
 * EXERCISE 5: Shadowing
 * ---------------------
 * Explain why the outputs differ.
 */

console.log('\n=== Exercise 5: Shadowing ===');

var name = 'Global';

function greetGlobal() {
  console.log('Hello, ' + name);
}

function greetLocal() {
  var name = 'Local';
  console.log('Hello, ' + name);
}

function greetWithParameter(name) {
  console.log('Hello, ' + name);
}

greetGlobal();
greetLocal();
greetWithParameter('Parameter');

// YOUR PREDICTION:
// greetGlobal(): Hello, ___
// greetLocal(): Hello, ___
// greetWithParameter("Parameter"): Hello, ___

/*
 * SOLUTION:
 * greetGlobal(): Hello, Global
 * greetLocal(): Hello, Local
 * greetWithParameter("Parameter"): Hello, Parameter
 *
 * Local variables and parameters "shadow" outer variables.
 */

/**
 * EXERCISE 6: TDZ (Temporal Dead Zone)
 * ------------------------------------
 * Predict which lines will throw errors.
 */

console.log('\n=== Exercise 6: TDZ ===');

function exercise6() {
  // Uncomment each line one at a time to test

  // Line A:
  // console.log(varVariable);  // What happens?
  var varVariable = 'var';

  // Line B:
  // console.log(letVariable);  // What happens?
  let letVariable = 'let';

  // Line C:
  // console.log(constVariable);  // What happens?
  const constVariable = 'const';

  console.log('All variables:', varVariable, letVariable, constVariable);
}

exercise6();

// YOUR PREDICTION:
// Line A throws error? ___
// Line B throws error? ___
// Line C throws error? ___

/*
 * SOLUTION:
 * Line A: No error - logs "undefined" (var is hoisted)
 * Line B: ReferenceError - let is in TDZ
 * Line C: ReferenceError - const is in TDZ
 */

/**
 * EXERCISE 7: Creation Phase Quiz
 * -------------------------------
 * Write what the execution context looks like after the creation phase.
 */

console.log('\n=== Exercise 7: Creation Phase Quiz ===');

function quiz(param) {
  var local = 'local value';
  function nested() {}
  var another;
}

// quiz(42);

// Write the creation phase state:
/*
 * YOUR ANSWER:
 * {
 *   arguments: ___,
 *   param: ___,
 *   local: ___,
 *   nested: ___,
 *   another: ___,
 *   this: ___
 * }
 */

/*
 * SOLUTION:
 * {
 *   arguments: { 0: 42, length: 1 },
 *   param: 42,
 *   local: undefined,
 *   nested: function nested() {},
 *   another: undefined,
 *   this: global object (or undefined in strict mode)
 * }
 */

/**
 * EXERCISE 8: Multiple Calls
 * --------------------------
 * Trace the execution contexts created.
 */

console.log('\n=== Exercise 8: Multiple Calls ===');

var counter = 0;

function increment() {
  counter++;
  console.log('Counter:', counter);
}

increment();
increment();
increment();

// Question: How many Function Execution Contexts were created total?
// YOUR ANSWER: ___

/*
 * SOLUTION: 3 Function Execution Contexts
 *
 * Each call to increment() creates a new FEC.
 * They all share the same global 'counter' variable.
 */

/**
 * EXERCISE 9: Identify the Bug
 * ----------------------------
 * Why doesn't this work as expected?
 */

console.log('\n=== Exercise 9: Identify the Bug ===');

function createFunctions() {
  var result = [];

  for (var i = 0; i < 3; i++) {
    result.push(function () {
      console.log('Value:', i);
    });
  }

  return result;
}

var funcs = createFunctions();
funcs[0](); // Expected: 0
funcs[1](); // Expected: 1
funcs[2](); // Expected: 2

// Question: What actually gets logged? Why?
// YOUR ANSWER: ___

// Fix the bug (write your solution):
function createFunctionsFixed() {
  var result = [];

  // YOUR CODE HERE

  return result;
}

/*
 * SOLUTION:
 * All three log "Value: 3"
 *
 * Why: 'var i' creates ONE variable in the function's context.
 * All closures reference the SAME 'i', which is 3 after the loop.
 *
 * Fix using let:
 */
function createFunctionsFixed() {
  var result = [];

  for (let i = 0; i < 3; i++) {
    result.push(function () {
      console.log('Value:', i);
    });
  }

  return result;
}

/**
 * EXERCISE 10: 'this' in Execution Context
 * ----------------------------------------
 * Predict the value of 'this' in each case.
 */

console.log("\n=== Exercise 10: 'this' Binding ===");

const person = {
  name: 'Alice',

  sayName: function () {
    console.log("sayName 'this'.name:", this.name);
  },

  sayNameArrow: () => {
    console.log("sayNameArrow 'this':", this);
  },

  delayed: function () {
    setTimeout(function () {
      console.log("setTimeout callback 'this':", this);
    }, 0);
  },

  delayedArrow: function () {
    setTimeout(() => {
      console.log("setTimeout arrow 'this'.name:", this.name);
    }, 0);
  },
};

person.sayName();
person.sayNameArrow();
// person.delayed();      // Uncomment to test
// person.delayedArrow(); // Uncomment to test

// YOUR PREDICTIONS:
// sayName 'this'.name: ___
// sayNameArrow 'this': ___
// setTimeout callback 'this': ___
// setTimeout arrow 'this'.name: ___

/*
 * SOLUTION:
 * sayName 'this'.name: "Alice" (method call, this = person)
 * sayNameArrow 'this': global object (arrow inherits outer 'this')
 * setTimeout callback 'this': global/undefined (regular function in setTimeout)
 * setTimeout arrow 'this'.name: "Alice" (arrow preserves outer 'this')
 */

/**
 * EXERCISE 11: Build an Execution Context
 * ---------------------------------------
 * Manually trace through this code and build the execution contexts.
 */

console.log('\n=== Exercise 11: Build Execution Context ===');

var color = 'blue';

function changeColor(newColor) {
  var color = newColor;

  function verify() {
    return color;
  }

  return verify();
}

var result = changeColor('red');
console.log(result);

// Draw the execution contexts:
/*
 * Global EC:
 *   - color: ___
 *   - changeColor: ___
 *   - result: ___
 *
 * changeColor("red") EC:
 *   - arguments: ___
 *   - newColor: ___
 *   - color: ___
 *   - verify: ___
 *
 * verify() EC:
 *   - (empty, uses outer scope)
 */

/*
 * SOLUTION:
 * Global EC:
 *   - color: "blue"
 *   - changeColor: function
 *   - result: "red" (after changeColor returns)
 *
 * changeColor("red") EC:
 *   - arguments: { 0: "red", length: 1 }
 *   - newColor: "red"
 *   - color: "red"
 *   - verify: function
 *
 * verify() EC:
 *   - arguments: { length: 0 }
 *   - Looks up scope chain, finds color = "red"
 */

/**
 * EXERCISE 12: Strict Mode Differences
 * ------------------------------------
 * Predict the behavior in strict vs non-strict mode.
 */

console.log('\n=== Exercise 12: Strict Mode ===');

function nonStrictTest() {
  return typeof this;
}

function strictTest() {
  'use strict';
  return typeof this;
}

console.log('Non-strict function called directly:', nonStrictTest());
console.log('Strict function called directly:', strictTest());

// YOUR PREDICTION:
// Non-strict: ___
// Strict: ___

/*
 * SOLUTION:
 * Non-strict: "object" (this is global object)
 * Strict: "undefined" (this is undefined in strict mode)
 */

/**
 * EXERCISE 13: Hoisting Order
 * ---------------------------
 * What gets logged and why?
 */

console.log('\n=== Exercise 13: Hoisting Order ===');

function hoistOrder() {
  console.log(typeof value);

  var value = "I'm a string";

  function value() {
    return "I'm a function";
  }

  console.log(typeof value);
}

hoistOrder();

// YOUR PREDICTION:
// First typeof: ___
// Second typeof: ___

/*
 * SOLUTION:
 * First typeof: "function"
 * Second typeof: "string"
 *
 * Explanation:
 * - During creation phase, function declarations are hoisted first
 * - Then var declarations (but 'value' already exists, so ignored)
 * - During execution, var value = "string" overwrites the function
 */

/**
 * EXERCISE 14: Scope Chain Lookup
 * -------------------------------
 * Trace how the scope chain finds each variable.
 */

console.log('\n=== Exercise 14: Scope Chain Lookup ===');

var a = 1;

function first() {
  var b = 2;

  function second() {
    var c = 3;

    function third() {
      var d = 4;
      console.log(a, b, c, d);
    }

    third();
  }

  second();
}

first();

// Trace for each variable in third():
// - d is found in: ___
// - c is found in: ___
// - b is found in: ___
// - a is found in: ___

/*
 * SOLUTION:
 * - d is found in: third()'s own scope
 * - c is found in: second()'s scope (1 level up)
 * - b is found in: first()'s scope (2 levels up)
 * - a is found in: global scope (3 levels up)
 */

/**
 * EXERCISE 15: Create Isolated Contexts
 * -------------------------------------
 * Use IIFEs to create separate execution contexts with the same variable names.
 */

console.log('\n=== Exercise 15: Create Isolated Contexts ===');

// Create three IIFEs, each with their own 'secret' variable
// that logs different values

// YOUR CODE HERE:
/*
(function() {
    var secret = ???;
    console.log(secret);
})();

(function() {
    var secret = ???;
    console.log(secret);
})();

(function() {
    var secret = ???;
    console.log(secret);
})();
*/

/*
 * SOLUTION:
 */
(function () {
  var secret = 'First secret';
  console.log(secret);
})();

(function () {
  var secret = 'Second secret';
  console.log(secret);
})();

(function () {
  var secret = 'Third secret';
  console.log(secret);
})();

// Each IIFE has its own execution context with its own 'secret'

console.log('\n=== Exercises Complete ===');
console.log('Review your answers against the solutions!');
Exercises - JavaScript Tutorial | DeepML