No history yet

Execution Context and Hoisting

How JavaScript Runs Your Code

When you give a script to the JavaScript engine, it doesn't just start running your code from the first line to the last. Before a single line is executed, the engine does a quick first pass to prepare everything. This entire process happens within something called an Execution Context.

Think of an Execution Context as a container built by the JavaScript engine to manage the code it's currently running. The most fundamental one is the Global Execution Context (GEC). This is the default context for any code that isn't inside a function. It's the base level of your program.

Every line of JavaScript runs inside an Execution Context — this is the foundation of how JS works.

The life of the GEC has two distinct phases: a Creation Phase and an Execution Phase. Understanding the difference between these two is key to understanding some of JavaScript's most peculiar behaviors.

Phase 1: The Creation Phase

Before your code runs, the engine scans through it to find all the variable and function declarations. Its goal is to set aside memory for them. This process creates what's known as the Variable Environment.

During this phase, the engine performs a neat trick called hoisting. It takes your variable and function declarations and conceptually moves them to the top of their scope. This is why you can sometimes use a variable or call a function before it's declared in your code.

With var declarations, the engine creates the variable in memory and gives it a placeholder value of undefined. It doesn't know the actual value yet—that happens during the execution phase.

console.log(myVar); // Logs: undefined

var myVar = 10;

console.log(myVar); // Logs: 10

The first console.log doesn't throw an error because, thanks to hoisting, the engine knows myVar exists. It just hasn't been assigned the value 10 yet.

Function declarations are hoisted even more completely. The entire function body is placed into memory, which allows you to call the function before its physical location in the code.

sayHello(); // Logs: "Hello, world!"

function sayHello() {
  console.log("Hello, world!");
}

let, const, and the TDZ

The hoisting behavior of var can lead to confusing bugs. To address this, let and const were introduced in ES6. They are also hoisted, but they behave differently. They are not initialized with undefined.

Instead, they exist in a state called the (TDZ) from the start of their scope until the point where they are declared. If you try to access a let or const variable while it's in the TDZ, JavaScript will throw a ReferenceError.

// This will throw a ReferenceError
console.log(bestLanguage); 

let bestLanguage = "JavaScript";

This error is a good thing. It prevents you from accidentally using a variable before its value has been set, making your code more robust and predictable.

Phase 2: The Execution Phase

Once the creation phase is complete and all memory has been allocated, the engine finally begins the execution phase. It goes through your code line by line and executes it.

This is when variable assignments happen. The engine sees myVar = 10 and updates the value in memory from undefined to 10. This synchronous, line-by-line execution is the default behavior of JavaScript. Even though JavaScript is famous for its asynchronous capabilities, its fundamental execution model is single-threaded and synchronous.

Quiz Questions 1/5

What are the two distinct phases of the Global Execution Context in JavaScript?

Quiz Questions 2/5

What happens to a variable declared with var during the Creation Phase?

Understanding this two-phase process is foundational. It demystifies hoisting and explains why variables declared with var, let, and const behave differently, setting the stage for more complex concepts like scope and closures.