javascript

examples

examples.js
/**
 * ============================================
 * 6.1 EXECUTION CONTEXT - EXAMPLES
 * ============================================
 */

/**
 * EXAMPLE 1: Global Execution Context
 * ------------------------------------
 * When JavaScript starts, it creates the Global EC
 */

// These are in the Global Execution Context
var globalVar = "I'm a global variable";
let globalLet = "I'm also global but not on window";
const globalConst = "I'm a global constant";

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

// In browsers, var and functions attach to window
console.log('=== Global Execution Context ===');
console.log('globalVar:', globalVar);
console.log('globalFunction:', globalFunction());

// Check if we're in browser or Node
if (typeof window !== 'undefined') {
  console.log('window.globalVar:', window.globalVar);
  console.log('window.globalLet:', window.globalLet); // undefined!
}

/**
 * EXAMPLE 2: Function Execution Context
 * -------------------------------------
 * New EC created each time a function is CALLED
 */

console.log('\n=== Function Execution Context ===');

function createMessage(name) {
  // New FEC created here with:
  // - arguments: { 0: name, length: 1 }
  // - name: (parameter value)
  // - greeting: undefined → "Hello"
  // - this: global/undefined

  var greeting = 'Hello';
  return greeting + ', ' + name + '!';
}

console.log(createMessage('Alice')); // FEC created
console.log(createMessage('Bob')); // Another FEC created

/**
 * EXAMPLE 3: Creation Phase - Hoisting
 * ------------------------------------
 * Variables and functions are set up before code runs
 */

console.log('\n=== Creation Phase (Hoisting) ===');

// This works because of the creation phase
console.log('sayHello exists:', typeof sayHello); // "function"
sayHello(); // Works!

function sayHello() {
  console.log('Hello from hoisted function!');
}

// var is hoisted but undefined
console.log('myVar before assignment:', myVar); // undefined
var myVar = 'Now I have a value';
console.log('myVar after assignment:', myVar); // "Now I have a value"

/**
 * EXAMPLE 4: Execution Phase
 * --------------------------
 * Code runs line by line
 */

console.log('\n=== Execution Phase ===');

function calculate(a, b) {
  // Creation Phase:
  // - a: 5, b: 3
  // - result: undefined
  // - doubled: undefined

  // Execution Phase:
  console.log('Step 1: result is', typeof result); // undefined

  var result = a + b;
  console.log('Step 2: result is', result); // 8

  var doubled = result * 2;
  console.log('Step 3: doubled is', doubled); // 16

  return doubled;
}

calculate(5, 3);

/**
 * EXAMPLE 5: Nested Execution Contexts
 * ------------------------------------
 * Each function call creates its own context
 */

console.log('\n=== Nested Execution Contexts ===');

function outer(x) {
  var outerVar = 'outer: ' + x;
  console.log('In outer, outerVar =', outerVar);

  function inner(y) {
    var innerVar = 'inner: ' + y;
    console.log('In inner, innerVar =', innerVar);
    console.log('In inner, can access outerVar =', outerVar);
  }

  inner(x * 2);
}

outer(5);

/**
 * EXAMPLE 6: Variable Environment vs Lexical Environment
 * -------------------------------------------------------
 * var vs let/const behavior
 */

console.log('\n=== Variable Environment vs Lexical Environment ===');

function demonstrateEnvironments() {
  // var: stored in Variable Environment
  // Hoisted and initialized to undefined
  console.log('varVariable before:', varVariable); // undefined
  var varVariable = "I'm var";
  console.log('varVariable after:', varVariable);

  // let/const: stored in Lexical Environment
  // Hoisted but NOT initialized (Temporal Dead Zone)
  // console.log("letVariable before:", letVariable); // ReferenceError!
  let letVariable = "I'm let";
  console.log('letVariable after:', letVariable);
}

demonstrateEnvironments();

/**
 * EXAMPLE 7: Temporal Dead Zone
 * -----------------------------
 * The zone where let/const exist but can't be accessed
 */

console.log('\n=== Temporal Dead Zone ===');

function tdzExample() {
  // TDZ starts for 'myLet' and 'myConst'

  // This would throw ReferenceError:
  // console.log(myLet);

  console.log("Before let declaration - can't access myLet");

  let myLet = 'Now accessible';
  // TDZ ends for myLet

  console.log('After let declaration:', myLet);

  const myConst = 'Constant value';
  console.log('After const declaration:', myConst);
}

tdzExample();

/**
 * EXAMPLE 8: Scope Chain Through Execution Contexts
 * -------------------------------------------------
 * How variables are looked up
 */

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

var globalName = 'Global';

function levelOne() {
  var levelOneName = 'Level One';

  function levelTwo() {
    var levelTwoName = 'Level Two';

    function levelThree() {
      var levelThreeName = 'Level Three';

      // Can access all outer scopes
      console.log('From levelThree:');
      console.log('  levelThreeName:', levelThreeName);
      console.log('  levelTwoName:', levelTwoName);
      console.log('  levelOneName:', levelOneName);
      console.log('  globalName:', globalName);
    }

    levelThree();
  }

  levelTwo();
}

levelOne();

/**
 * EXAMPLE 9: Each Call Gets Its Own Context
 * -----------------------------------------
 * Same function, different contexts
 */

console.log('\n=== Separate Execution Contexts ===');

function counter(id) {
  var count = 0;
  count++;
  console.log(`Counter ${id}: count = ${count}`);
  return count;
}

// Each call has its own 'count' variable
counter('A'); // count = 1
counter('B'); // count = 1 (not 2!)
counter('C'); // count = 1 (still independent)

/**
 * EXAMPLE 10: this in Execution Context
 * -------------------------------------
 * 'this' is determined at context creation
 */

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

// Global context
console.log("Global 'this':", this === globalThis); // true in most environments

const myObject = {
  name: 'MyObject',

  regularMethod: function () {
    // 'this' = myObject (determined by how it's called)
    console.log("Regular method 'this'.name:", this.name);
  },

  arrowMethod: () => {
    // 'this' = lexical 'this' (from where arrow was defined)
    console.log("Arrow method 'this':", this);
  },
};

myObject.regularMethod(); // "MyObject"
myObject.arrowMethod(); // global object or undefined

/**
 * EXAMPLE 11: Hoisting Order of Precedence
 * ----------------------------------------
 * Functions are hoisted before variables
 */

console.log('\n=== Hoisting Precedence ===');

function hoistingTest() {
  console.log('typeof myFunc:', typeof myFunc); // "function" (not undefined)

  var myFunc = "I'm a string now";

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

  console.log('typeof myFunc:', typeof myFunc); // "string"
}

hoistingTest();

/**
 * EXAMPLE 12: Arguments Object in Function EC
 * -------------------------------------------
 * The arguments object is created in the creation phase
 */

console.log('\n=== Arguments Object ===');

function showArguments(a, b, c) {
  console.log('Named parameters:', a, b, c);
  console.log('arguments object:', arguments);
  console.log('arguments.length:', arguments.length);
  console.log('arguments[0]:', arguments[0]);

  // arguments is array-like but not an array
  console.log('Is array:', Array.isArray(arguments)); // false
}

showArguments(1, 2, 3);
showArguments('x', 'y'); // c will be undefined

/**
 * EXAMPLE 13: Closure and Execution Context
 * -----------------------------------------
 * Closures remember their creation context
 */

console.log('\n=== Closure and Execution Context ===');

function createGreeter(greeting) {
  // This execution context is "remembered"

  return function (name) {
    // New EC, but has reference to outer EC
    return `${greeting}, ${name}!`;
  };
}

const sayHi = createGreeter('Hi');
const sayHowdy = createGreeter('Howdy');

// createGreeter's EC is gone, but closure remembers 'greeting'
console.log(sayHi('Alice')); // "Hi, Alice!"
console.log(sayHowdy('Bob')); // "Howdy, Bob!"

/**
 * EXAMPLE 14: Debugging with Execution Context Knowledge
 * ------------------------------------------------------
 * Understanding why common bugs happen
 */

console.log('\n=== Common Bugs Explained ===');

// Bug 1: Accessing before declaration
function bug1() {
  console.log('accessingVar:', accessingVar); // undefined, not error
  var accessingVar = 'value';
  // Why? Creation phase hoists var to undefined
}
bug1();

// Bug 2: Loop variable issue (classic)
function bug2() {
  var funcs = [];

  for (var i = 0; i < 3; i++) {
    funcs.push(function () {
      return i; // All return 3!
    });
  }

  console.log(
    'With var:',
    funcs.map((f) => f())
  ); // [3, 3, 3]
  // Why? Only one 'i' in the function's execution context
}
bug2();

// Fix: Use let (block-scoped)
function bug2Fixed() {
  var funcs = [];

  for (let i = 0; i < 3; i++) {
    funcs.push(function () {
      return i;
    });
  }

  console.log(
    'With let:',
    funcs.map((f) => f())
  ); // [0, 1, 2]
  // Why? New 'i' for each iteration's block scope
}
bug2Fixed();

/**
 * EXAMPLE 15: Strict Mode and Execution Context
 * ---------------------------------------------
 * Strict mode affects 'this' binding
 */

console.log("\n=== Strict Mode and 'this' ===");

function nonStrict() {
  console.log("Non-strict 'this':", typeof this); // object (global)
}

function useStrict() {
  'use strict';
  console.log("Strict 'this':", this); // undefined
}

nonStrict();
useStrict();

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