JavaScript uses Static Scoping. This means variable visibility is determined by the physical location of the code in the source file. The scope of a variable is determined by where we type the function rather than where we call the function. Because it is based on the text structure the engine determines the scope hierarchy during the Creation Phase.
The Scope of a function is physically managed by an Execution Context. This frame is composed of two distinct Environment Records. The combination of these records linked via pointers to their parents forms the Scope Chain.
This is the legacy storage. It tracks variables declared with var and formal function declarations. A Function Execution Context has only one Variable Environment. It ignores block levels and is scoped strictly to the function level.
This record was introduced in ES6 to support block-scoping. It tracks variables declared with let and const. While the Function Execution Context has a base Lexical Environment every time the engine enters a block such as an if statement or a for loop it creates a new nested Lexical Environment for that block.
The Scope Chain is a linked list of environments. It relies on two critical internal pointers to bridge the gap between the Stack and the Heap.
[[Environment]] PointerThis pointer is stored inside the Function Object in the Heap memory. It stores a reference to the Lexical Environment Record where the function was physically defined. This is the memory of the function. It ensures that even if a function is called later from a different location it remembers the variables of its parent. In the case of closures this pointer refers to the persistent Context Object in the Heap.
outer PointerThis pointer is stored in the active stack frame of an Execution Context. It stores a reference to the immediate parent lexical environment record. When a function is invoked the engine copies the value from the [[Environment]] pointer in the heap into the outer pointer of the new stack frame.
When we try to access a variable the JavaScript Engine follows a strict algorithm to find the value.
let or const. If the variable is not found it checks the Variable Environment of that same frame for var or function declarations.outer pointer to the parent lexical environment record.outer pointers until the Global Execution Context is reached.Shadowing occurs when a variable in a local scope has the same name as a variable in an outer scope. The resolution algorithm finds the variable immediately in the local environment and stops searching which effectively hides the outer version.
let user = "Global";
function login() {
let user = "Local";
console.log(user);
}| Concept | Location | Physical Content |
|---|---|---|
| Variable Env | Stack Frame | Stores var and function declarations for the whole function. |
| Lexical Env | Stack Frame | Stores let and const for specific blocks. |
| [[Environment]] | Function Heap | Reference to the parent Lexical Environment where the function was born. |
| outer pointer | Stack Frame | Reference used to walk up to the immediate parent environment record. |