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:

  1. Scope Chain: Variable lookup mechanism
  2. Compilation Phase: Scope determination
  3. Block Scope: let and const behavior
  4. Function Scope: var and function declarations
  5. 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:

  1. Scope Chain: Nested function scope access
  2. Block Scope: let/const scoping behavior
  3. Module Pattern: Private state through scope
  4. Factory Functions: Encapsulation with scope
  5. Async Operations: Scope in async contexts
  6. Performance: Scope lookup implications
  7. Module Systems: Scope-based modularity
  8. 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.