Skip to main content

1. Variables, Scope & Hoisting

This guide explains JavaScript variables, scope, and hoisting with examples.

Variables (var, let, const)

JavaScript provides three ways to declare variables.
KeywordScopeRedeclareReassignHoistedNotes
varFunction scopeYesYesYes (undefined)Legacy
letBlock scopeNoYesYes (TDZ)Modern
constBlock scopeNoNoYes (TDZ)Must initialize

var Example

function variables() {
  var a = 10;
  if (true) {
    a = 15;
    var c = 10;
  }

  var a = 15;
  console.log(a); // 15
  console.log(c); // 10

  console.log(b); // undefined (hoisted)
  var b = 10;
}

variables();

let Example

function lets() {
  let a = 10;
  if (true) {
    a = 12;
  }
  console.log(a); // 12

  console.log(b); // ReferenceError
  let b = 10;
}

lets();

const Example

function constants() {
  const a = 10;
  console.log(a);

  console.log(b); // ReferenceError
  const b = 12;

  b = 15; // TypeError
}

constants();

Scope

JavaScript has three scopes.

Global Scope

var globalVar = "I am global";
console.log(globalVar); // OK

Function Scope

function checkScope() {
  var functionVar = "Inside function";
  console.log(functionVar);
}

checkScope();
console.log(functionVar); // ReferenceError

Block Scope

if (true) {
  let x = 10;
  const y = 20;
}
console.log(x); // ReferenceError
console.log(y); // ReferenceError

Hoisting

Hoisting moves declarations to the top of their scope.
TypeHoisted?Value
varYesundefined
letYesTDZ
constYesTDZ
FunctionYesFully available

Hoisting Example

console.log(x); // undefined
var x = 10;

console.log(y); // ReferenceError
let y = 20;

console.log(z); // ReferenceError
const z = 30;

hoistedFunction(); // OK
function hoistedFunction() {
  console.log("I am hoisted!");
}

2. Closures and lexical environment

Lexical Environment

A lexical environment is a structure where variable and function identifiers are stored and resolved.
It is created automatically by JavaScript whenever code is executed.
A lexical environment has two parts:

Environment Record

Holds:
  • Variables
  • Functions
  • Parameters

Outer Environment Reference

A pointer to the parent lexical environment, enabling scope chaining.

Lexical Environments Creation

Global Lexical Environment

Created at the start of the script.

Function Lexical Environment

Created every time a function is invoked.

Block Lexical Environment

Created for block-scoped declarations (let, const) inside:
  • if
  • for
  • {} blocks

How JavaScript Resolves Variables

When JavaScript looks up a variable:
  1. It checks the current lexical environment.
  2. If missing, it checks the outer environment.
  3. Continues upward until global scope is reached.
This upward traversal is the scope chain.

Simple Example

let a = 10;

function outer() {
  let b = 20;

  function inner() {
    let c = 30;
    console.log(a, b, c);
  }

  inner();
}

outer();

Lexical Environment Chain:

  • inner() → has c, outer = outer()
  • outer() → has b, outer = global
  • global → has a

Summary

A lexical environment:
  • Stores identifiers and their values
  • Exists for global, function, and block scopes
  • Forms the basis of JavaScript’s scope chain
  • Ensures variables are resolved using their lexical (written-in-code) position

Closures

A closure happens when:
  • A function remembers variables from its parent function
  • Even after the parent function has returned / finished executing
  • In short: Closure = Function + Remembered Lexical Environment

function outer() {
  let a = 12;

  function inner() {
    console.log(a);
  }

  return inner; // return the function, not its result
}

const funct = outer(); // outer() finishes, but inner still remembers "a"
funct(); // 12