JS Guide
HomeQuestionsTopicsCompaniesResources
BookmarksSearch

Built for developers preparing for JavaScript, React & TypeScript interviews.

ResourcesQuestionsSupport
HomeQuestionsSearchProgress
HomeTopicsjavascriptScope & Scope Chain
PrevNext
javascript
intermediate
12 min read

Scope & Scope Chain

block-scope
closures
function-scope
iife
lexical-scoping
scope
scope-chain
shadowing

JavaScript uses lexical scoping where variable access is determined by code structure at write time — the engine walks up the scope chain from inner to outer scopes until it finds the variable or reaches the global scope.

Key Points

1Scope Chain

Variable lookup walks outward from the current scope through each enclosing scope until the global scope — this chain is fixed at write time.

2Block vs Function Scope

var is function-scoped (ignores blocks), while let/const are block-scoped (confined to nearest {}). This is the most tested scope distinction.

3Lexical Scoping

A function's scope is determined by where it is defined, not where it is called — this is why closures work.

4Variable Shadowing

An inner scope variable with the same name hides the outer variable — the outer still exists but is inaccessible from the inner scope.

5Module Scope

ES modules create their own scope — top-level declarations are not global and must be explicitly exported.

What You'll Learn

  • Understand how the scope chain resolves variable lookups
  • Distinguish between block scope, function scope, and global scope
  • Explain why lexical scoping enables closures
  • Identify and fix scope-related bugs like the classic var loop problem

Deep Dive

Scope determines where variables and functions are accessible in your code. JavaScript uses lexical (static) scoping, meaning scope is determined by the physical location of code when it's written, not when it's executed.

Types of Scope

Global scope — Variables declared outside any function or block are globally accessible. In browsers, var declarations at the top level attach to window. Polluting global scope with too many variables causes naming collisions and hard-to-debug issues.

Function scope — Variables declared with var inside a function are accessible anywhere within that function, including nested blocks. Before ES6, function scope was the only way to create local variables.

Block scope — Variables declared with let and const are confined to the nearest enclosing {} block (if, for, while, or standalone blocks). This was introduced in ES6 and is the default scoping behavior in modern JavaScript.

Module scope — ES modules create their own scope. Top-level declarations in a module are not added to the global scope — they're only accessible within the module unless explicitly exported.

The Scope Chain

When JavaScript encounters a variable reference, it searches for the variable starting from the current scope and walking outward through each enclosing scope until it reaches the global scope. This chain of scopes is called the scope chain.

JavaScript
const global = 'G';
function outer() {
  const outerVar = 'O';
  function inner() {
    const innerVar = 'I';
    console.log(innerVar); // Found in inner scope
    console.log(outerVar); // Found in outer scope (1 level up)
    console.log(global);   // Found in global scope (2 levels up)
  }
  inner();
}

If the variable is not found in any scope, a ReferenceError is thrown (in strict mode) or an accidental global is created (in sloppy mode with assignment).

Lexical vs Dynamic Scoping

JavaScript uses lexical scoping — a function's scope is determined by where it is defined, not where it is called. This is why closures work: a function retains access to its defining scope's variables even when called from a different scope.

The only exception is this, which is determined dynamically at call time (unless arrow functions or .bind() are used).

Variable Shadowing

When an inner scope declares a variable with the same name as an outer scope variable, the inner variable shadows the outer one. The outer variable still exists but is inaccessible from the inner scope.

JavaScript
const x = 'outer';
function example() {
  const x = 'inner'; // Shadows outer x
  console.log(x);    // 'inner'
}
example();
console.log(x);      // 'outer' — unchanged

IIFE Pattern

Before ES6, the Immediately Invoked Function Expression was the primary way to create isolated scope and avoid polluting the global namespace:

JavaScript
(function() {
  var private = 'hidden';
  // This variable doesn't leak to global scope
})();

With let, const, and ES modules, IIFEs are rarely needed in modern code, but they still appear in legacy codebases and some library patterns.

The Classic Loop Problem

The most common scope-related interview question involves var in a for loop:

JavaScript
for (var i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 100);
}
// Prints: 3, 3, 3 — because var is function-scoped, all callbacks share one i
 
for (let i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 100);
}
// Prints: 0, 1, 2 — because let creates a new binding per iteration

Key Interview Distinction: Lexical Scope Enables Closures Scope and closures are deeply connected. A closure is a function that remembers its lexical scope. Understanding the scope chain is prerequisite to understanding why closures retain access to outer variables — the function carries a reference to its defining scope, not a copy of the variables.

Fun Fact

Brendan Eich was hired by Netscape in 1995 specifically to embed the Scheme programming language in the browser. Scheme's key innovation was lexical scoping — where a function's scope is determined by where it's written, not where it's called. When management forced Eich to make the language look like Java instead, he kept Scheme's lexical scoping as the foundation. This one decision is why closures work in JavaScript and why modern patterns like React hooks are possible.

Learn These First

Variables

beginner

Continue Learning

Closures

intermediate

Hoisting

beginner

Practice What You Learned

What is the difference between block scope and function scope?
mid
scope
Function scope (var) means a variable is accessible throughout the entire function it's declared in. Block scope (let/const) limits a variable to the nearest enclosing block (if, for, while, or bare {}). Block scoping prevents common bugs like loop variable leaking and accidental overwrites.
Previous
Variables
Next
IIFE (Immediately Invoked Function Expression)
PrevNext