JavaScript Scope: Global, Function, Block, and Lexical Scope

Scope in JavaScript determines where variables, functions, and other identifiers can be accessed in a program. Understanding scope helps you avoid naming conflicts, prevent unexpected behavior, and write clean, maintainable code.

JavaScript has several important scope concepts, including global scope, function scope, block scope, and lexical scope. The scope chain explains how JavaScript searches for variables when they are used inside nested functions or blocks.

In this tutorial, you will learn how each type of scope works, how var, let, and const behave differently, and how JavaScript finds variables through the scope chain.

What Is Scope in JavaScript?

Scope is the area of a program where a variable or function is accessible.

For example:

const message = "Hello, JavaScript!";

function greet() {
  console.log(message);
}

greet();

Output:

Hello, JavaScript!

The variable message is declared outside the function. Because it belongs to the outer scope, the greet() function can access it.

However, a variable declared inside a function is generally not accessible outside that function.

function greet() {
  const message = "Hello, JavaScript!";
  console.log(message);
}

greet();

console.log(message);

The final statement throws a ReferenceError because message is local to the function.

JavaScript scope rules determine which parts of a program can access each variable.

What Is Global Scope in JavaScript?

Global scope is the outermost scope available to ordinary JavaScript code within a particular execution environment or script context.

A variable declared at the top level of a classic browser script using var, let, or const is accessible to code in that script unless another scope shadows it.

Example of Global Scope

const siteName = "Web4Learners";

function displaySiteName() {
  console.log(siteName);
}

displaySiteName();

console.log(siteName);

Output:

Web4Learners
Web4Learners

The variable siteName is declared outside the function, so it can be accessed both inside displaySiteName() and from the outer script.

Global Variables in the Browser

In a classic browser script, a top-level var declaration creates a property on the global window object.

var userName = "Karim";

console.log(window.userName);

Output:

Karim

However, top-level let and const declarations in classic scripts do not create corresponding properties on window.

let age = 22;
const country = "India";

console.log(window.age);
console.log(window.country);

Output:

undefined
undefined

The variables still exist in the script’s global lexical environment. They simply are not properties of window.

Important: JavaScript modules have their own module scope. Top-level variables declared in a module do not automatically become global variables or properties of window.

Advantages and Disadvantages of Global Scope

Global variables can be useful when information needs to be shared across a broad part of a program. However, excessive use of global variables can cause problems.

Advantages:

  • Values can be accessed from many parts of the same script context.
  • Shared configuration can be easy to reference.
  • Simple examples can be easier to understand.

Disadvantages:

  • Different parts of a program may accidentally reuse the same names.
  • Functions can change shared values unexpectedly.
  • Large applications become harder to maintain.
  • Unnecessary global state can make testing more difficult.

Use local variables and modules when possible, and reserve global scope for values that genuinely need to be globally accessible.

What Is Function Scope in JavaScript?

Function scope means that a variable declared inside a function is accessible within that function and its nested scopes, but not from outside the function.

Variables declared with var, let, and const inside a function are all restricted to that function or its nested scopes.

Example of Function Scope

function calculateTotal() {
  const price = 500;
  const tax = 50;

  console.log(price + tax);
}

calculateTotal();

Output:

550

The variables price and tax are available inside calculateTotal().

Trying to access them outside the function causes a ReferenceError.

function calculateTotal() {
  const price = 500;
}

calculateTotal();

console.log(price);

The variable price is not defined in the outer scope.

Function Scope With var

The var keyword is function-scoped rather than block-scoped.

function showMessage() {
  var message = "Hello from a function";

  console.log(message);
}

showMessage();

Output:

Hello from a function

The message variable cannot be accessed outside showMessage().

Nested Functions and Function Scope

A nested function can access variables declared in its containing function.

function outerFunction() {
  const outerMessage = "Outer function";

  function innerFunction() {
    console.log(outerMessage);
  }

  innerFunction();
}

outerFunction();

Output:

Outer function

The inner function can access outerMessage because the variable is defined in its outer lexical environment.

This is an important part of lexical scope, which we will explore later in this tutorial.

What Is Block Scope in JavaScript?

Block scope means that a variable is accessible only within the block in which it is declared and any nested scopes.

A block is usually defined by a pair of curly braces {}. Blocks can appear in if statements, loops, and standalone code blocks.

The let and const keywords are block-scoped. The var keyword is not.

Example of Block Scope With let

if (true) {
  let message = "Inside the block";

  console.log(message);
}

Output:

Inside the block

The variable message is accessible inside the if block.

Trying to access it outside the block causes a ReferenceError.

if (true) {
  let message = "Inside the block";
}

console.log(message);

The variable exists only within the block where it was declared.

Block Scope With const

The const keyword also creates block-scoped variables.

if (true) {
  const siteName = "Web4Learners";

  console.log(siteName);
}

Output:

Web4Learners

The siteName variable cannot be accessed outside the block.

Remember that const prevents reassignment of the variable binding, but it does not make an object or array immutable.

Difference Between var, let, and const in Blocks

Consider this example:

if (true) {
  var firstName = "Karim";
  let lastName = "Shaik";
  const age = 22;
}

console.log(firstName);

Output:

Karim

The var variable is accessible outside the block because var is not block-scoped.

However, accessing lastName or age outside the block would cause a ReferenceError.

KeywordScope inside a functionBlock-scopedCan be reassigned
varYesNoYes
letYesYesYes
constYesYesNo, the binding cannot be reassigned

Block Scope in Loops

Block scope is especially useful in loops.

for (let i = 1; i <= 3; i++) {
  console.log(i);
}

Output:

1
2
3

The variable i is scoped to the loop.

Accessing i after the loop finishes causes a ReferenceError.

By contrast, using var would make the loop variable accessible in the surrounding function or script scope.

What Is Lexical Scope in JavaScript?

Lexical scope means that the accessibility of variables is determined by where functions and blocks are written in the source code.

A function can access variables from its own scope and from the surrounding scopes in which it was defined.

JavaScript does not determine a function’s outer variable scope based on where the function is called. It is based on where the function was created.

Example of Lexical Scope

const language = "JavaScript";

function outerFunction() {
  const message = "Learning";

  function innerFunction() {
    console.log(message, language);
  }

  innerFunction();
}

outerFunction();

Output:

Learning JavaScript

The innerFunction() function can access both message and language.

  • message is defined in the containing function.
  • language is defined in the global scope.

This is lexical scope in action.

Lexical Scope With Nested Functions

Consider a function that contains another function.

function outer() {
  const name = "Karim";

  function inner() {
    const greeting = "Hello";

    console.log(greeting, name);
  }

  inner();
}

outer();

Output:

Hello Karim

The inner function can access greeting from its own scope and name from the outer function’s scope.

The outer function cannot access the inner function’s local variable greeting.

Lexical Scope Does Not Depend on Where a Function Is Called

This example demonstrates a key rule.

const name = "Global";

function outer() {
  const name = "Outer";

  return function inner() {
    console.log(name);
  };
}

const result = outer();

function runFunction(callback) {
  const name = "Caller";
  callback();
}

runFunction(result);

Output:

Outer

Why does the output say "Outer" instead of "Caller"?

The inner() function was defined inside outer(). Therefore, its lexical environment includes the name variable declared in outer().

Calling it from runFunction() does not change the scope in which it was created.

This principle is fundamental to understanding closures and callback functions.

What Is the Scope Chain in JavaScript?

The scope chain is the sequence of lexical environments JavaScript searches when resolving a variable name.

When JavaScript encounters a variable reference, it first looks in the current scope. If the name is not found, it searches the enclosing scope, then the next outer scope, continuing until it finds the name or reaches the outermost environment.

If the name cannot be resolved, JavaScript throws a ReferenceError when the unresolved identifier is evaluated.

Example of the Scope Chain

const country = "India";

function outerFunction() {
  const state = "Telangana";

  function innerFunction() {
    const city = "Hyderabad";

    console.log(city);
    console.log(state);
    console.log(country);
  }

  innerFunction();
}

outerFunction();

Output:

Hyderabad
Telangana
India

The innerFunction() function accesses variables from three levels:

  • Its own scope contains city.
  • Its outer function scope contains state.
  • The global scope contains country.

JavaScript follows the scope chain to find each variable.

How JavaScript Searches for Variables

<box border radius=”lg” padding={3} gap={3}> <box background=”surface-secondary” radius=”md” padding={3} align=”center” gap={1}> **Inner Function Scope**

<text size="sm">`city = "Hyderabad"`</text>
<badge color="info">Search starts here</badge>

</box> <box align=”center” gap={1}> <icon name=”arrow-down” size=”lg” /> <text color=”secondary” size=”sm”>If the variable is not found</text> </box> <box background=”surface” border radius=”md” padding={3} align=”center” gap={1}> **Outer Function Scope**

<text size="sm">`state = "Telangana"`</text>

</box> <box align=”center” gap={1}> <icon name=”arrow-down” size=”lg” /> <text color=”secondary” size=”sm”>If the variable is still not found</text> </box> <box background=”surface” border radius=”md” padding={3} align=”center” gap={1}> **Global Scope**

<text size="sm">`country = "India"`</text>

</box> </box>

This is a simplified illustration of lexical lookup. If a variable is not found in any accessible enclosing environment, the lookup fails.

Variable Shadowing

Variable shadowing occurs when a variable declared in an inner scope has the same name as a variable in an outer scope.

const message = "Global message";

function showMessage() {
  const message = "Local message";

  console.log(message);
}

showMessage();

console.log(message);

Output:

Local message
Global message

Inside showMessage(), JavaScript finds the local message first. It does not continue to the outer variable because a matching name has already been found.

Outside the function, the global message remains unchanged.

Shadowing is valid in many situations, but confusing or excessive reuse of names can make code harder to understand.

How Scope Works With Closures

A closure is created when a function retains access to variables from its lexical environment, even after the outer function has finished executing.

Closures rely on lexical scope and the scope chain.

Example of a Closure

function createCounter() {
  let count = 0;

  return function () {
    count++;
    return count;
  };
}

const counter = createCounter();

console.log(counter());
console.log(counter());
console.log(counter());

Output:

1
2
3

The createCounter() function returns an inner function. Although createCounter() has finished executing, the returned function retains access to count.

Each call to counter() updates the same variable.

Why Closures Are Useful

Closures can be used to:

  • Preserve state between function calls.
  • Create functions with private internal variables.
  • Build reusable callbacks.
  • Implement function factories.
  • Manage event handlers and asynchronous operations.

For example, a counter closure allows the count variable to remain accessible to the returned function without exposing it as a global variable.

Common Scope Mistakes in JavaScript

Accessing a Function Variable From Outside

Incorrect:

function calculate() {
  const total = 100;
}

calculate();

console.log(total);

The code throws a ReferenceError because total is defined inside the function.

Correct:

function calculate() {
  const total = 100;
  return total;
}

console.log(calculate());

Output:

100

Returning a value allows the caller to receive the result without directly accessing the function’s local variable.

Assuming var Is Block-Scoped

Incorrect assumption:

if (true) {
  var score = 90;
}

console.log(score);

Output:

90

The variable is accessible because var is not block-scoped.

Use let or const when you want a variable limited to the block.

Expecting the Calling Function to Define Lexical Scope

A function uses the lexical environment in which it was created, not the local variables of whichever function calls it.

This is why the earlier inner() example prints "Outer" rather than "Caller".

Confusing Scope With Object Properties

A variable declared inside a function is not automatically a property of an object. Scope determines where a name can be resolved; object properties are accessed through an object.

These are different concepts, even though both involve accessing values.

Global Scope vs Function Scope vs Block Scope vs Lexical Scope

Scope conceptWhat it meansExample
Global scopeVariables accessible across the relevant script or environmentconst siteName = "Web4Learners";
Function scopeVariables declared inside a function are local to that functionfunction test() { const x = 10; }
Block scopelet and const are limited to their enclosing blockif (true) { let x = 10; }
Lexical scopeVariable accessibility follows where code is writtenAn inner function accesses variables from its outer function
Scope chainThe sequence of outer environments searched for a variableInner scope → outer scope → global environment

Lexical scope describes how the environment is determined by the source code. The scope chain describes how JavaScript searches those environments to resolve identifiers.

Frequently Asked Questions

What is scope in JavaScript?

Scope determines where variables, functions, and other identifiers can be accessed within a program.

What is the difference between global scope and function scope?

Global scope makes a declaration available throughout its relevant script or environment, subject to its declaration rules. Function scope restricts a variable to the function in which it is declared and its nested scopes.

What is the difference between function scope and block scope?

Function scope applies to declarations such as var inside a function. Block scope restricts let and const to the block where they are declared.

What is lexical scope in JavaScript?

Lexical scope means a function’s access to outer variables is determined by where the function is defined in the source code, not by where it is called.

What is the scope chain?

The scope chain is the sequence of lexical environments JavaScript searches to resolve a variable reference, starting with the current environment and moving outward.

What is variable shadowing?

Variable shadowing occurs when a declaration in an inner scope uses the same name as a declaration in an outer scope. The inner declaration takes precedence within its scope.

How are closures related to scope?

Closures retain access to variables from the lexical environment in which a function was created. This allows functions to preserve state even after an outer function has returned.

Conclusion

JavaScript scope determines where variables can be accessed and how identifiers are resolved.

Global scope makes declarations available across a broad execution context, function scope keeps local variables inside functions, and block scope limits let and const to specific blocks. Lexical scope determines which outer variables a function can access, while the scope chain explains how JavaScript searches for those variables.

Once you understand these concepts, closures, callbacks, event handlers, and modern JavaScript applications become much easier to understand and debug.