Understanding Scope and Closures in JavaScript

What is Scope in JavaScript?

Scope in JavaScript refers to the current context of execution. It defines where variables, functions, and objects are accessible. JavaScript uses lexical scoping, meaning scope is determined by the placement of variables and functions in the source code.

Types of Scope

  1. Global Scope
  2. Local (Function) Scope
  3. Block Scope
  4. Lexical Scope
  5. Closures
  6. Module Scope

1.Global Scope

Variables declared outside of any function or block are in the global scope. They can be accessed anywhere in the program, including inside functions.


// Global variable
let name = 'Priyanshu';

function greet() {
  console.log(`Hi, ${name}`); // Accessible
}

greet();
console.log(name); // Accessible globally
  

2. Local (Function) Scope

Variables declared inside a function are local to that function. They cannot be accessed from outside the function.


function showAge() {
  const age = 21;
  console.log(age); // Output: 21
}

showAge();
console.log(age); //  ReferenceError
  

3. Block Scope

Variables declared with let or const inside a block { } are block-scoped. They are not accessible outside the block.


{
  let course = 'JavaScript';
  const year = 2025;
  console.log(course); // JavaScript
}

console.log(course); // ReferenceError

// var is NOT block scoped
{
  var oldWay = 'Global var';
}
console.log(oldWay); // Accessible
  

4. Lexical Scope

Lexical scope means functions have access to variables defined in their outer scope — based on where the function was defined, not where it's called.


function outer() {
  let outerVar = 'I am outer';

  function inner() {
    console.log(outerVar); // Accesses outerVar
  }

  inner();
}

outer();
console.log(outerVar); // ReferenceError
  

5. Closures

A closure is formed when an inner function "remembers" the variables from its outer function, even after the outer function has finished executing.

This allows data privacy and is used in many powerful JavaScript patterns.

Example 1: Counter using Closure


function createCounter() {
  let count = 0;

  function increment() {
    count++;
    console.log(`Count: ${count}`);
  }

  return increment;
}

const counter1 = createCounter();
counter1(); // Count: 1
counter1(); // Count: 2

const counter2 = createCounter();
counter2(); // Count: 1 (separate instance)
  

Example 2: Closure with Parameters


function multiplyBy(n) {
  return function(x) {
    return n * x;
  }
}

const double = multiplyBy(2);
console.log(double(5)); // 10

const triple = multiplyBy(3);
console.log(triple(4)); // 12
  

6. Module Scope

In JavaScript modules (using type="module" or separate JS files), variables declared are scoped to the module. They are not accessible globally unless exported.


// File: myModule.js
const secret = 'Private';
export const name = 'Sudhanshu';

export function greet() {
  console.log(`Hi, ${name}`);
}

// File: main.js
import { greet } from './myModule.js';
greet(); // Hi, Sudhanshu
  

Implicit Globals

Declaring variables without let, const, or var creates implicit global variables. This can lead to bugs and unpredictable behavior.


function outer() {
  age = 21; // Implicit global
  console.log(age); // 21
}
outer();
console.log(age); // 21 — but it's now global (bad practice)
  

Preventing Implicit Globals with Strict Mode

Use 'use strict' to avoid silent bugs and enforce variable declarations.


'use strict';

function test() {
  undeclaredVar = 10; //  ReferenceError
}
test();
  

Important Points to Remember

Scope Type Comparison Table


| Scope Type     | Accessible In          | Declared Using            | Notes                           |
|----------------|------------------------|----------------------------|----------------------------------|
| Global         | Anywhere               | Outside any function       | Can be accessed from any scope   |
| Function Scope | Inside the function    | var, let, const            | Not accessible outside           |
| Block Scope    | Inside { }             | let, const                 | Not accessible outside the block |
| Lexical Scope  | Defined in source code | Nested functions           | Scope determined where declared  |
| Closure        | Retains outer scope    | Function inside function   | Keeps access after return        |
| Module Scope   | Within a module        | let, const (in modules)    | Not accessible globally          |