Lexical Scope
Lexical scope defines how variable names are resolved in nested functions. Understanding lexical scope is crucial for mastering closures and writing maintainable JavaScript code.
Core Concepts
Key Components:
- Scope Chain: Variable lookup mechanism
- Compilation Phase: Scope determination
- Block Scope: let and const behavior
- Function Scope: var and function declarations
- Global Scope: Window/global object
Implementation and Best Practices
// Scope Chain Example
function outer() {
const x = 10
function middle() {
const y = 20
function inner() {
const z = 30
// Access to x, y, and z
console.log(x + y + z)
}
inner()
}
middle()
}
// Block Scope Implementation
class BlockScopeDemo {
private static scopeTest(): void {
let x = 10
if (true) {
let x = 20 // Different x
console.log(x) // 20
}
console.log(x) // 10
}
private static scopeConflict(): void {
const x = 10
// Would cause error:
// {
// const x = 20
// {
// const x = 30
// // Each x is distinct
// }
// }
}
}
// Module Pattern Using Lexical Scope
const Counter = (function () {
let count = 0 // Private due to lexical scope
return {
increment() {
return ++count
},
decrement() {
return --count
},
getCount() {
return count
},
}
})()
// Factory Function with Private State
function createPerson(name: string) {
// Private variables due to lexical scope
let age = 0
const privateData = {
secretId: Math.random(),
}
return {
getName: () => name,
getAge: () => age,
setAge: (newAge: number) => {
if (newAge >= 0) {
age = newAge
}
},
}
}
// Scope and Async Operations
function asyncScope() {
const messages: string[] = []
// Wrong way - var in loop creates closure issues
for (var i = 0; i < 3; i++) {
setTimeout(() => {
messages.push(`Value: ${i}`)
}, 100)
}
// Correct way - let creates new binding each iteration
for (let i = 0; i < 3; i++) {
setTimeout(() => {
messages.push(`Value: ${i}`)
}, 100)
}
return messages
}
// Scope Chain Performance
class ScopePerformance {
private static readonly ITERATIONS = 1000000
static measureScopeLookup(): void {
const global = 'global'
function inner() {
let localAccess = 0
let globalAccess = 0
const local = 'local'
console.time('scope-lookup')
for (let i = 0; i < this.ITERATIONS; i++) {
// Local variable access (faster)
localAccess += local.length
// Global/outer scope access (slower)
globalAccess += global.length
}
console.timeEnd('scope-lookup')
}
inner()
}
}
// Dynamic Scope Simulation
class DynamicScope {
private context: Map<string, any> = new Map()
setValue(name: string, value: any): void {
this.context.set(name, value)
}
getValue(name: string): any {
return this.context.get(name)
}
execute(fn: () => void): void {
fn.call(this)
}
}
// Scope-Safe Constructors
function User(name: string) {
if (!(this instanceof User)) {
return new User(name)
}
this.name = name
}
// Module System with Lexical Privacy
class ModuleSystem {
private static modules = new Map<string, any>()
static define(
name: string,
deps: string[],
factory: (...deps: any[]) => any,
): void {
const resolvedDeps = deps.map((dep) => this.modules.get(dep))
this.modules.set(name, factory(...resolvedDeps))
}
static get(name: string): any {
return this.modules.get(name)
}
}
// Example Usage
function example() {
// Counter Module
console.log(Counter.getCount()) // 0
Counter.increment() // 1
Counter.increment() // 2
console.log(Counter.getCount()) // 2
// Person Factory
const person = createPerson('John')
person.setAge(30)
console.log(person.getName()) // "John"
console.log(person.getAge()) // 30
// Module System
ModuleSystem.define('math', [], () => ({
add: (a: number, b: number) => a + b,
}))
ModuleSystem.define('calculator', ['math'], (math) => ({
square: (n: number) => math.add(n, n),
}))
const calculator = ModuleSystem.get('calculator')
console.log(calculator.square(5)) // 10
// Scope Performance Test
ScopePerformance.measureScopeLookup()
}
The implementations above demonstrate various aspects of lexical scope:
- Scope Chain: Nested function scope access
- Block Scope: let/const scoping behavior
- Module Pattern: Private state through scope
- Factory Functions: Encapsulation with scope
- Async Operations: Scope in async contexts
- Performance: Scope lookup implications
- Module Systems: Scope-based modularity
- Safe Construction: Scope-safe patterns
Each implementation shows proper scope management while maintaining code organization and performance. These patterns help create maintainable and efficient JavaScript applications.