JavaScript Scope, Hoisting, and Execution Context
Understanding variable resolution and execution phases in JavaScript
JavaScript Scope, Hoisting, and Execution Context
In this document, I want to explain how scope, hoisting, and execution context interact in JavaScript, and how variable references are resolved during execution.
1. Scope
Scope defines the region of code where a variable is accessible.
-
JavaScript has function scope and block scope.
var
→ function scope onlylet
andconst
→ block scope ({}
)
-
Accessing variables outside their scope results in a ReferenceError.
JavaScript follows lexical scope rules:
- Scope is determined at code-writing time by where variables are defined, not where functions are called.
- Inner scopes can access variables from outer scopes.
- Outer scopes cannot access variables from inner scopes.
2. Hoisting
Hoisting is the behavior where declarations are processed before execution. This gives the impression that variables and functions are “moved” to the top of their scope.
var
: declaration is hoisted and initialized toundefined
.let
andconst
: declarations are hoisted but remain uninitialized. Accessing them before initialization causes a ReferenceError due to the Temporal Dead Zone (TDZ).
console.log(a); // undefined
var a = 10;
console.log(b); // ReferenceError
let b = 20;
Hoisting exists because JavaScript engines parse code before executing it. During parsing, declarations are recorded in memory, allowing them to be referenced earlier than their written position.
3. Variable Declarations (var
, let
, const
)
-
var
- Function scope
- Hoisted and initialized with
undefined
- Redeclaration allowed
-
let
- Block scope
- Hoisted but not initialized (TDZ applies)
- Redeclaration not allowed
-
const
- Block scope
- Hoisted but not initialized (TDZ applies)
- Must be initialized at declaration
4. Execution Context and Lexical Environment
Whenever code runs, the JavaScript engine creates an execution context.
-
Each execution context contains a Lexical Environment with:
- Environment Record → stores variables and their bindings
- Outer Lexical Environment Reference → points to the parent scope
5. Variable Resolution Process
When a variable is referenced:
- The engine looks in the current scope’s environment record.
- If not found, it follows the outer lexical environment reference.
- This continues until the global scope.
- If not found globally, a ReferenceError is thrown.
This explains why inner scopes can access outer variables, but outer scopes cannot access inner variables.
function outer() {
const a = 1;
function inner() {
console.log(a); // 1 (found in outer scope)
}
inner();
}
outer();
console.log(a); // ReferenceError (outer cannot access inner)