Lexical Scoping & Scope Chain
MedLexical scope means that scope is determined by where variables and functions are declared in the source code (at write time), not where they are called (at runtime). This creates a static scope chain that never changes. Understanding lexical scope is the foundation for understanding closures - a function always has access to the scope where it was defined, no matter where it is executed.
Interactive Visualization
Scope Nesting
Variables declared outside any function/block
Key Points
- Lexical = "where written" not "where called"
- Scope chain: inner scope → outer scope → global
- Variable lookup follows the scope chain until found
- Scope is static - doesn't change based on how function is called
- This is different from "this" binding which is dynamic
- Lexical scope enables closures
Code Examples
Lexical vs Dynamic Scope
const x = "global"; function outer() { const x = "outer"; return inner; } function inner() { console.log(x); // "global" - looks at where DEFINED, not called! } const fn = outer(); fn(); // "global" - inner's scope is determined at definition // With dynamic scope, this would print "outer" // JavaScript uses lexical scope, not dynamic scope
inner() uses the scope where it was defined, not where outer() was called
Scope Chain Lookup
const a = "global-a"; function level1() { const b = "level1-b"; function level2() { const c = "level2-c"; console.log(a); // "global-a" - found in global console.log(b); // "level1-b" - found in level1 console.log(c); // "level2-c" - found in current scope } level2(); } level1(); // Lookup path for 'a': level2 → level1 → global ✅ Found!
JavaScript walks up the scope chain looking for variables
Lexical Scope Never Changes
function createFunction() { const message = "Hello from createFunction"; return function() { console.log(message); }; } const fn = createFunction(); // Even though createFunction has finished executing... // fn still has access to its scope! fn(); // "Hello from createFunction" // The scope chain is fixed at definition time // and preserved (closure)
The returned function carries its lexical scope with it - this is closure
Shadowing with Lexical Scope
const value = "global"; function outer() { const value = "outer"; function inner() { // const value = "inner"; console.log(value); // "outer" - finds nearest in scope chain } inner(); } outer();
Inner scope shadows outer scope - JavaScript finds the nearest definition in the lexical scope chain
Scope Chain vs This Binding
const obj = { name: "Object", method: function() { console.log(this.name); // "Object" - dynamic binding const arrow = () => { console.log(this.name); // "Object" - arrow uses lexical this }; arrow(); } }; const fn = obj.method; fn(); // this.name = undefined (or global name) // But arrow still uses obj's scope // Scope (lexical) ≠ this (dynamic)
Scope is lexical (static), but this is dynamic. Arrow functions use lexical this.
Common Mistakes
- Confusing lexical scope with dynamic scope
- Expecting scope to change based on where function is called
- Confusing scope chain with prototype chain
- Confusing scope with this binding
Interview Tips
- Emphasize: scope is determined at write time, this at runtime
- Trace scope chain explicitly in examples
- Contrast lexical scope with dynamic scope
- Connect lexical scope to closures