🌙
☀️ Dark
PART 4

JavaScript

Variables, closures, prototypes, event loop, promises.

Intermediate 45 min read

Volume 4: JavaScript Engine, Core mechanics, and Asynchronous Systems

Learning Objectives

By the end of this volume, you will be able to:

  • Understand exactly how JavaScript variables, types, and coercion work at the engine level.
  • Reason about execution contexts, scope chains, hoisting, and closures.
  • Master the object-oriented nature of JS via prototypes and modern ES6+ classes.
  • Demystify the JavaScript Event Loop, microtasks, macrotasks, and asynchronous execution (Promises, async/await).
  • Interact directly with the DOM, Browser APIs, and the network via Fetch.
  • Understand memory management, garbage collection (GC), and ES Modules.

Prerequisites

Basic programming logic (variables, loops, conditions). Understanding of the web (HTTP basics, HTML/CSS). No prior JavaScript framework knowledge is required.

Why Does This Exist?

JavaScript was created in 10 days in 1995 to add simple interactivity to web pages. Today, it powers the modern web, high-performance backends (Node.js), desktop apps (Electron), and mobile apps (React Native). Understanding its idiosyncrasies is the difference between a framework user and a true engineer.

The Problem Before the Solution

Before JavaScript, the web was static. Every user interaction required a full round-trip to the server to render a new page. The naive solution was Java Applets or Flash, which required heavy plugins and were isolated from the DOM.

Why the Old Approach Breaks

Plugins were insecure, crashed frequently, and couldn't easily manipulate the native HTML elements. A native scripting language embedded in the browser was necessary to modify the DOM instantly.

History

Brendan Eich created Mocha, then LiveScript, and finally JavaScript at Netscape. It borrowed syntax from C/Java, first-class functions from Scheme, and prototypes from Self. Eventually standardized as ECMAScript (ES).

Mental Model (Analogy -> Reality)

Analogy: Think of JavaScript as a single chef (Single Thread) in a restaurant kitchen. The chef can only cook one dish at a time. If a dish needs to bake for 30 minutes, the chef doesn't wait; they put it in the oven (Web APIs), ask a waiter to let them know when it's done (Event Queue), and start chopping vegetables for the next order. When the oven dings, the waiter puts the baked dish at the front of the chef's to-do list (Event Loop).

Reality: JavaScript is single-threaded and non-blocking. It delegates long-running tasks (network, timers) to the host environment (browser C++ APIs or Node.js libuv) and uses an Event Loop to push callbacks back onto the Call Stack when the thread is free.

Internal Working (Memory, stack, process, network, etc.)

Memory: Primitives (strings, numbers, booleans) are stored on the Stack (or inline). Objects/Arrays are stored in the Heap, with a reference (pointer) on the Stack.

Garbage Collection (GC): V8 (Chrome/Node engine) uses a Generational Garbage Collector. It traverses objects starting from root nodes (global object). Unreachable objects are swept away.

Execution Context: Each function call creates an Execution Context containing its Variable Environment (local variables), a reference to the outer environment (Scope Chain), and the value of this.

Visual Explanation (ASCII diagrams)

[ JS Engine (V8) ]
+---------------------+    +-------------------+
|     Heap (Memory)   |    | Web APIs / Node C++|
|  { objects, arrays }|    | - DOM, fetch, setTimeout
+---------------------+    +-------------------+
          |                           |
+---------------------+               |
|     Call Stack      |               |
| [ functionC() ]     |               |
| [ functionB() ]     |               |
| [ functionA() ]     |               |
| [ Global Context]   |               |
+---------------------+               |
          ^                           v
          |                 +-------------------+
          |--- Event Loop --| Task Queue (Macrotask)|
                            +-------------------+
                            | Microtask Queue   | (Promises)
                            +-------------------+

Syntax

// Variables, Scope, Hoisting
let blockScoped = 'ES6';
const constantRef = {}; // Reference cannot change, object can mutate
var functionScoped = 'Avoid using'; // Hoisted with 'undefined'

// Functions & Closures
const outer = (x) => {
  return function inner(y) { return x + y; } // closure captures x
};

// Objects, Prototypes, Classes
class Engineer {
  constructor(name) { this.name = name; }
  build() { console.log('Building...'); }
}
const e = new Engineer('Alice'); // Under the hood: sets Engineer.prototype

// Arrays, Destructuring
const stack = ['HTML', 'CSS', 'JS'];
const [markup, ...rest] = stack;

// Async/Await, Promises
async function fetchData(url) {
  try {
    const res = await fetch(url);
    return await res.json();
  } catch (err) {
    console.error(err);
  }
}

Tiny Example

Understanding Coercion and Closures:

function createCounter() {
  let count = 0;
  return function() {
    count++; // count is kept alive via closure
    return count + ""; // Type coercion: Number + String = String
  }
}

Walkthrough

1. We declare createCounter.
2. Calling it creates a new Execution Context with count = 0.
3. It returns an anonymous function. The Execution Context of createCounter is destroyed from the stack, but the count variable survives in memory because the inner function retains a reference to it (closure).
4. When we call the inner function, it increments count and coerces it to a string by adding "".

Break It

What happens if we rely on global scope instead of closures?

let count = 0;
function counter() { return count++; }
// Another script can modify `count` accidentally, causing unpredictable state!

Debug It

Use console.dir(counterFunction) in the browser console. Inspect the [[Scopes]] property. You will explicitly see a Closure (createCounter) object containing count.

Mini Project

Build a simple Virtual DOM diffing algorithm that accepts an old tree and a new tree (plain JavaScript objects representing HTML nodes), compares them recursively (using closures for state and arrays for children), and applies the changes to the actual DOM via Browser APIs.

Real Application Feature

Implementing a robust API Client using ES Modules, fetch, and asynchronous patterns with proper error handling and request deduplication.

Production Implementation

// api.js (ES Module)
const requestCache = new Map();

export async function fetchWithCache(url) {
  if (requestCache.has(url)) return requestCache.get(url);
  
  try {
    const response = await fetch(url);
    if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
    const data = await response.json();
    requestCache.set(url, data);
    return data;
  } catch (error) {
    console.error('Fetch failed', error);
    throw error;
  }
}

Production Usage

In production, you bundle ES modules (using Webpack/Vite) to reduce network waterfall. You use strict mode ("use strict") to prevent silent global variable declarations. You polyfill modern APIs if supporting older browsers.

Performance

Microtasks vs Macrotasks: Promises queue Microtasks. setTimeout queues Macrotasks. Microtasks drain completely before the Event Loop moves to the next Macrotask. An infinite loop of Promises (Microtasks) will block rendering. Avoid generating excessive objects in hot loops to prevent Garbage Collection pauses.

Best Practices

  • Use const by default, let when rebinding is needed. Never use var.
  • Avoid implicit coercion; use === instead of ==.
  • Do not mutate global prototypes (like Array.prototype).
  • Handle async errors using try/catch inside async functions.

Interview Questions

Easy: What is the difference between let, const, and var?

var is function-scoped and hoisted with initialization (undefined). let and const are block-scoped and hoisted without initialization (Temporal Dead Zone). const cannot be reassigned.

Medium: How does `this` work in JavaScript?

this is determined by how a function is called, not where it is defined. In methods, it's the object calling it. In simple functions, it's global (or undefined in strict mode). Arrow functions lexically bind this to the surrounding scope.

Hard: Explain the output of this Event Loop question: `setTimeout(()=>console.log(1),0); Promise.resolve().then(()=>console.log(2)); console.log(3);`

Output: 3, 2, 1. Synchronous code runs first (3). Promise then() pushes to the Microtask queue. setTimeout pushes to the Macrotask queue. Microtasks run before the next Macrotask (2, then 1).

Senior: How does V8 optimize JavaScript execution?

V8 compiles JS directly to machine code using an interpreter (Ignition) to start running quickly. Hot code paths are sent to a JIT compiler (TurboFan) which generates optimized machine code based on type assumptions (Hidden Classes/Shapes). If the types change later, V8 de-optimizes back to bytecode.

Engineering Challenge

Write a polyfill for Promise.all from scratch. It must handle arrays of mixed promises and raw values, resolve when all are complete in the correct order, and reject immediately if any single promise rejects.

View Solution
function myPromiseAll(promises) {
  return new Promise((resolve, reject) => {
    let results = [];
    let completed = 0;
    if (promises.length === 0) return resolve(results);
    
    promises.forEach((p, index) => {
      Promise.resolve(p).then(value => {
        results[index] = value;
        completed++;
        if (completed === promises.length) resolve(results);
      }).catch(reject);
    });
  });
}

Revision Sheet

Variables: var (fn scope), let/const (block scope).
Coercion: `+` prefers strings, `-` prefers numbers.
Closures: Functions bundled with their lexical environment.
Prototypes: Objects linked to other objects via `__proto__`.
Event Loop: Call Stack -> Microtasks (Promises) -> Macrotasks (Timers/Events) -> Render.

Connections

This chapter connects directly to understanding Node.js (which shares the V8 engine and Event Loop) and modern frontend frameworks like React (which heavily rely on closures, destructuring, and asynchronous DOM updates).

Mini Project (20-30 min)

▶ View Solution

Implementation

javascript
// code here

Bigger Project (1-2 hours)

Build a comprehensive project for this chapter's topic.

▶ View Solution

Full implementation details

typescript
// code here
🏠 Curriculum NextVolume 2